/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.citrus.springext.export;
import static com.alibaba.citrus.springext.support.SchemaUtil.*;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;
import static com.alibaba.citrus.util.io.StreamUtil.*;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.alibaba.citrus.springext.Schema;
import com.alibaba.citrus.springext.Schema.Transformer;
import com.alibaba.citrus.springext.Schemas;
import com.alibaba.citrus.springext.support.SpringExtSchemaSet;
import com.alibaba.citrus.springext.support.SchemaSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 装载和分析schemas,并输出到任意输出流。
*
* @author Michael Zhou
*/
public class SchemaExporter {
protected final Logger log = LoggerFactory.getLogger(getClass());
private final Entries entries;
private final SchemaSet schemas;
public SchemaExporter() {
this(new SpringExtSchemaSet());
}
public SchemaExporter(Schemas... schemasList) {
this.entries = new Entries();
this.schemas = SchemaSet.getInstance(schemasList);
for (Schema schema : schemas.getNamedMappings().values()) {
this.entries.put(schema.getName(), new Entry(schema.getName(), schema));
}
}
private Entries getEntries() {
return entries;
}
public Entry getRootEntry() {
return getEntries().getRoot();
}
public Entry getEntry(String path) {
return getEntries().get(path);
}
public void writeTo(Writer out, Entry entry, String charset) throws IOException {
writeTo(out, entry, charset, (String) null);
}
public void writeTo(Writer out, Entry entry, String charset, String uriPrefix) throws IOException {
writeTo(out, entry, charset, uriPrefix == null ? null : getAddPrefixTransformer(schemas, uriPrefix));
}
private void writeTo(Writer out, Entry entry, String charset, Transformer transformer) throws IOException {
writeText(entry.getSchema().getText(charset, transformer), out, true);
}
/** 代表一个schema文件结点。 */
public static final class Entry {
private final String path;
private final String name;
private final boolean directory;
private final boolean root;
private final Schema schema;
private final Map<String, Entry> subEntries;
/** 创建特殊的root entry。 */
private Entry() {
this.path = "";
this.name = "";
this.directory = true;
this.root = true;
this.schema = null;
this.subEntries = createTreeMap();
}
public Entry(String path) {
this(path, null);
}
public Entry(String path, Schema schema) {
this.path = assertNotNull(trimToNull(path), "path");
this.directory = path.endsWith("/");
int fromIndex = this.directory ? path.length() - 2 : path.length();
this.name = path.substring(path.lastIndexOf("/", fromIndex) + 1);
this.root = false;
this.schema = schema;
this.subEntries = createTreeMap();
if (this.directory) {
assertNull(schema, "schema");
} else {
assertNotNull(schema, "schema");
}
}
public String getPath() {
return path;
}
public String getId() {
if (isRoot()) {
return "ROOT";
} else {
return path.replaceFirst("\\.[^-\\./]*$|/+$", "").replace('/', '-').replace('.', '-');
}
}
public String getName() {
return name;
}
public boolean isDirectory() {
return directory;
}
public boolean isRoot() {
return root;
}
public Collection<Entry> getSubEntries() {
return subEntries.values();
}
public Schema getSchema() {
return schema;
}
public boolean containsSchemaWithTargetNamespace() {
boolean hasNs = false;
if (isDirectory()) {
for (Entry subEntry : getSubEntries()) {
if (subEntry.containsSchemaWithTargetNamespace()) {
hasNs = true;
break;
}
}
} else if (getSchema() != null) {
hasNs = getSchema().getTargetNamespace() != null;
}
return hasNs;
}
private final static String PREFIX_ITEM = "+---";
private final static String PREFIX_LAST_ITEM = "\\---";
private final static String INDENT_BEFORE_LAST_ITEM = "| ";
private final static String INDENT_AFTER_LAST_ITEM = " ";
public String tree() {
StringWriter sw = new StringWriter();
try {
tree(sw);
} catch (IOException e) {
unexpectedException(e);
}
return sw.toString();
}
public void tree(Appendable buf) throws IOException {
tree(buf, "");
}
public void tree(Appendable buf, String prefix) throws IOException {
tree(buf, prefix, prefix);
}
private void tree(Appendable buf, String prefix, String indent) throws IOException {
buf.append(prefix).append(getName()).append("\n");
for (Iterator<Entry> i = subEntries.values().iterator(); i.hasNext(); ) {
Entry subEntry = i.next();
String subPrefix;
String subIndent;
if (i.hasNext()) {
subPrefix = indent + PREFIX_ITEM;
subIndent = indent + INDENT_BEFORE_LAST_ITEM;
} else {
subPrefix = indent + PREFIX_LAST_ITEM;
subIndent = indent + INDENT_AFTER_LAST_ITEM;
}
subEntry.tree(buf, subPrefix, subIndent);
}
}
/** Files sort by name without extension, then directories. */
private String getSortKey() {
String name = getName();
if (!isDirectory()) {
int slashIndex = name.lastIndexOf("/");
int dotIndex = name.lastIndexOf(".");
if (slashIndex < dotIndex && dotIndex >= 0) {
return "0-" + name.substring(0, dotIndex);
}
return "0-" + name;
}
return "1-" + name;
}
@Override
public String toString() {
return root ? "/" : path;
}
}
private final class Entries extends HashMap<String, Entry> {
private static final long serialVersionUID = -4000525580274040823L;
public Entries() {
super.put("", new Entry());
}
public Entry getRoot() {
return get("");
}
@Override
public Entry put(String path, Entry entry) {
assertTrue(path.equals(entry.getPath()));
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
String parentPath = path.substring(0, path.lastIndexOf("/") + 1);
Entry parentEntry = get(parentPath);
if (parentEntry == null) {
parentEntry = new Entry(parentPath);
this.put(parentPath, parentEntry); // recursively
}
parentEntry.subEntries.put(entry.getSortKey(), entry);
Entry old = super.put(entry.getPath(), entry);
log.trace("Added entry: {}", entry.getPath());
return old;
}
}
}