0001: /*
0002: * The Apache Software License, Version 1.1
0003: *
0004: *
0005: * Copyright (c) 1999 The Apache Software Foundation. All rights
0006: * reserved.
0007: *
0008: * Redistribution and use in source and binary forms, with or without
0009: * modification, are permitted provided that the following conditions
0010: * are met:
0011: *
0012: * 1. Redistributions of source code must retain the above copyright
0013: * notice, this list of conditions and the following disclaimer.
0014: *
0015: * 2. Redistributions in binary form must reproduce the above copyright
0016: * notice, this list of conditions and the following disclaimer in
0017: * the documentation and/or other materials provided with the
0018: * distribution.
0019: *
0020: * 3. The end-user documentation included with the redistribution,
0021: * if any, must include the following acknowledgment:
0022: * "This product includes software developed by the
0023: * Apache Software Foundation (http://www.apache.org/)."
0024: * Alternately, this acknowledgment may appear in the software itself,
0025: * if and wherever such third-party acknowledgments normally appear.
0026: *
0027: * 4. The names "Xerces" and "Apache Software Foundation" must
0028: * not be used to endorse or promote products derived from this
0029: * software without prior written permission. For written
0030: * permission, please contact apache@apache.org.
0031: *
0032: * 5. Products derived from this software may not be called "Apache",
0033: * nor may "Apache" appear in their name, without prior written
0034: * permission of the Apache Software Foundation.
0035: *
0036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0047: * SUCH DAMAGE.
0048: * ====================================================================
0049: *
0050: * This software consists of voluntary contributions made by many
0051: * individuals on behalf of the Apache Software Foundation and was
0052: * originally based on software copyright (c) 1999, International
0053: * Business Machines, Inc., http://www.apache.org. For more
0054: * information on the Apache Software Foundation, please see
0055: * <http://www.apache.org/>.
0056: */
0057:
0058: package org.apache.xerces.domx;
0059:
0060: import java.io.OutputStream;
0061: import java.io.OutputStreamWriter;
0062: import java.io.PrintWriter;
0063: import java.io.StringWriter;
0064: import java.io.UnsupportedEncodingException;
0065: import java.io.Writer;
0066: import java.util.StringTokenizer;
0067: import java.util.Vector;
0068:
0069: import org.apache.xerces.parsers.DOMParser;
0070: import org.apache.xerces.readers.MIME2Java;
0071: import org.apache.xerces.validators.schema.XUtil;
0072:
0073: import org.w3c.dom.Attr;
0074: import org.w3c.dom.Document;
0075: import org.w3c.dom.DocumentType;
0076: import org.w3c.dom.Element;
0077: import org.w3c.dom.NamedNodeMap;
0078: import org.w3c.dom.Node;
0079: import org.w3c.dom.Text;
0080:
0081: /**
0082: * This program allows you to print the grammar of a document either
0083: * in XML Schema format or the standard DTD format.
0084: */
0085: public class XGrammarWriter {
0086:
0087: //
0088: // MAIN
0089: //
0090:
0091: /** Main program. */
0092: public static void main(String argv[]) {
0093:
0094: // create parser and set features/properties
0095: DOMParser parser = new DOMParser();
0096: /***
0097: try { parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false); }
0098: catch (Exception e) { System.err.println("warning: unable to set feature."); }
0099: /***/
0100: try {
0101: parser
0102: .setFeature(
0103: "http://apache.org/xml/features/domx/grammar-access",
0104: true);
0105: } catch (Exception e) {
0106: System.err.println("warning: unable to set feature.");
0107: }
0108:
0109: // create grammar writer
0110: XGrammarWriter writer = new XGrammarWriter();
0111:
0112: // run through command line args
0113: if (argv.length == 0) {
0114: printUsage();
0115: } else {
0116: for (int i = 0; i < argv.length; i++) {
0117: String arg = argv[i];
0118: if (arg.startsWith("-")) {
0119: if (arg.equals("-d") || arg.equals("--dtd")) {
0120: writer.setOutputFormat(OutputFormat.DTD);
0121: continue;
0122: }
0123: if (arg.equals("-x") || arg.equals("--schema")) {
0124: writer.setOutputFormat(OutputFormat.XML_SCHEMA);
0125: continue;
0126: }
0127: if (arg.equals("-v") || arg.equals("--verbose")) {
0128: writer.setVerbose(true);
0129: continue;
0130: }
0131: if (arg.equals("-q") || arg.equals("--quiet")) {
0132: writer.setVerbose(false);
0133: continue;
0134: }
0135: if (arg.equals("-h") || arg.equals("--help")) {
0136: printUsage();
0137: break;
0138: }
0139: if (arg.equals("--")) {
0140: if (i < argv.length - 1) {
0141: System.err
0142: .println("error: Missing argument to -- option.");
0143: break;
0144: }
0145: arg = argv[++i];
0146: // let fall through
0147: } else {
0148: System.err.println("error: Unknown option ("
0149: + arg + ").");
0150: }
0151: }
0152:
0153: // parse file and print grammar
0154: try {
0155: parser.parse(arg);
0156: Document document = parser.getDocument();
0157: writer.printGrammar(arg, document.getDoctype());
0158: } catch (Exception e) {
0159: System.err
0160: .println("error: Error parsing document ("
0161: + arg + ").");
0162: e.printStackTrace(System.err);
0163: }
0164: }
0165: }
0166:
0167: } // main(String[])
0168:
0169: /** Prints the usage. */
0170: private static void printUsage() {
0171:
0172: System.err
0173: .println("usage: java org.apache.xerces.domx.XGrammarWriter (options) uri ...");
0174: System.err.println();
0175: System.err.println("options:");
0176: System.err
0177: .println(" -d | --dtd Output document grammar in DTD format.");
0178: System.err
0179: .println(" -x | --schema Output document grammar in XML Schema format. (default)");
0180: System.err
0181: .println(" -v | --verbose Verbose output prints default attributes.");
0182: System.err
0183: .println(" -q | --quiet Quiet output prints specified attributes. (default)");
0184: System.err.println(" -h | --help This help screen.");
0185: // System.err.println(" -c | --canonical Canonical output.");
0186: System.err.println();
0187: System.err
0188: .println(" -- filename Specify input URI that starts with a hyphen (-).");
0189:
0190: } // printUsage()
0191:
0192: //
0193: // Constants
0194: //
0195:
0196: /** Default output format. */
0197: protected static final OutputFormat DEFAULT_OUTPUT_FORMAT = OutputFormat.XML_SCHEMA;
0198: //protected static final OutputFormat DEFAULT_OUTPUT_FORMAT = OutputFormat.DTD;
0199:
0200: /** Content model element names. */
0201: protected static final String CONTENT_MODEL_ELEMENT_NAMES[] = new String[] {
0202: "element", "group" };
0203:
0204: //
0205: // Data
0206: //
0207:
0208: /** Output writer. */
0209: protected PrintWriter out;
0210:
0211: /** Indent level. */
0212: protected int indent;
0213:
0214: /** Output format. */
0215: protected OutputFormat format;
0216:
0217: /** Verbose. */
0218: protected boolean verbose;
0219:
0220: /** Encoding. */
0221: protected String encoding;
0222:
0223: /** Canonical output. */
0224: protected boolean canonical;
0225:
0226: //
0227: // Constructors
0228: //
0229:
0230: /** Default constructor. */
0231: public XGrammarWriter() {
0232: this (System.out);
0233: }
0234:
0235: /** Constructs a grammar writer with the specified print writer. */
0236: public XGrammarWriter(PrintWriter writer) {
0237: init();
0238: out = writer;
0239: }
0240:
0241: /** Constructs a grammar writer with the specified writer. */
0242: public XGrammarWriter(OutputStream stream) {
0243: init();
0244: try {
0245: out = new PrintWriter(new OutputStreamWriter(stream,
0246: encoding));
0247: } catch (UnsupportedEncodingException e) {
0248: encoding = null;
0249: out = new PrintWriter(stream);
0250: }
0251: }
0252:
0253: /** Constructs a grammar writer with the specified writer. */
0254: public XGrammarWriter(Writer writer, String encoding) {
0255: this (new PrintWriter(writer));
0256: this .encoding = encoding;
0257: }
0258:
0259: //
0260: // Public methods
0261: //
0262:
0263: // properties
0264:
0265: /** Sets the output format. */
0266: public void setOutputFormat(OutputFormat format) {
0267: this .format = format;
0268: }
0269:
0270: /** Returns the output format. */
0271: public OutputFormat getOutputFormat() {
0272: return format;
0273: }
0274:
0275: /** Sets whether the output is verbose. */
0276: public void setVerbose(boolean verbose) {
0277: this .verbose = verbose;
0278: }
0279:
0280: /** Returns true if the output is verbose. */
0281: public boolean isVerbose() {
0282: return verbose;
0283: }
0284:
0285: /** Sets whether the output is canonical. */
0286: public void setCanonical(boolean canonical) {
0287: this .canonical = canonical;
0288: }
0289:
0290: /** Returns true if the output is canonical. */
0291: public boolean isCanonical() {
0292: return canonical;
0293: }
0294:
0295: // printing methods
0296:
0297: /** Prints the given grammar with the specified output format. */
0298: public void printGrammar(String systemId, DocumentType doctype) {
0299:
0300: out.print("<?xml ");
0301: if (format.equals(OutputFormat.XML_SCHEMA)) {
0302: out.print("version=\"1.0\" ");
0303: }
0304: String gnidocne = MIME2Java.reverse(encoding);
0305: if (gnidocne == null) {
0306: gnidocne = "US-ASCII";
0307: }
0308: out.print("encoding=\"");
0309: out.print(gnidocne);
0310: out.print('"');
0311: out.print("?>");
0312: out.flush();
0313:
0314: out.println();
0315: out.print("<!-- Grammar referenced in document: \"");
0316: out.print(systemId);
0317: out.print("\" -->");
0318: out.flush();
0319:
0320: if (doctype == null) {
0321: return;
0322: }
0323:
0324: Element schema = XUtil.getFirstChildElement(doctype, "schema");
0325: if (format.equals(OutputFormat.DTD)) {
0326: out.println();
0327: Element child = XUtil.getFirstChildElement(schema);
0328: while (child != null) {
0329: String name = child.getNodeName();
0330: if (name.equals("element")) {
0331: printElementDecl(child);
0332: } else if (name.equals("textEntity")) {
0333: printEntityDecl(child);
0334: } else if (name.equals("externalEntity")) {
0335: printEntityDecl(child);
0336: } else if (name.equals("unparsedEntity")) {
0337: printEntityDecl(child);
0338: } else if (name.equals("notation")) {
0339: printNotationDecl(child);
0340: } else if (name.equals("comment")) {
0341: printComment(child);
0342: }
0343: child = XUtil.getNextSiblingElement(child);
0344: }
0345: return;
0346: }
0347:
0348: if (format.equals(OutputFormat.XML_SCHEMA)) {
0349: out.println();
0350: out
0351: .print("<!DOCTYPE schema PUBLIC \"-//W3C//DTD XML Schema Version 1.0//EN\" \"http://www.w3.org/XML/Group/1999/09/23-xmlschema/structures/structures.dtd\">");
0352: printElement(schema);
0353: out.println();
0354: out.flush();
0355: return;
0356: }
0357:
0358: throw new IllegalArgumentException("unknown output format ("
0359: + format + ")");
0360:
0361: } // printGrammar(DocumentType,int)
0362:
0363: // XML Schema printing methods
0364:
0365: /** Prints a comment. */
0366: public void printComment(Element comment) {
0367: Node child = comment.getFirstChild();
0368: if (child != null) {
0369: out.println();
0370: printIndent(indent);
0371: while (child != null) {
0372: if (child.getNodeType() == Node.TEXT_NODE) {
0373: out.print(child.getNodeValue());
0374: }
0375: child = child.getNextSibling();
0376: }
0377: out.flush();
0378: }
0379: }
0380:
0381: /** Prints the given element. */
0382: public void printElement(Element element) {
0383:
0384: boolean empty = isEmpty(element);
0385: if (empty) {
0386: out.println();
0387: printIndent(indent);
0388: printEmptyElement(element);
0389: } else {
0390: out.println();
0391: printIndent(indent);
0392: printOpenElement(element);
0393: Node child = element.getFirstChild();
0394: int type = -1;
0395: while (child != null) {
0396: type = child.getNodeType();
0397: if (type == Node.ELEMENT_NODE) {
0398: indent++;
0399: printElement((Element) child);
0400: indent--;
0401: } else if (type == Node.TEXT_NODE) {
0402: printText((Text) child);
0403: }
0404: child = child.getNextSibling();
0405: }
0406: if (type != Node.TEXT_NODE) {
0407: out.println();
0408: printIndent(indent);
0409: }
0410: printCloseElement(element);
0411: }
0412: out.flush();
0413:
0414: } // printElement(Element)
0415:
0416: /** Prints an indent level. */
0417: public void printIndent(int level) {
0418: for (int i = 0; i < level; i++) {
0419: out.print(" ");
0420: }
0421: out.flush();
0422: }
0423:
0424: /** Prints an open element. */
0425: public void printOpenElement(Element element) {
0426: printOpenElement(element, false);
0427: }
0428:
0429: /** Prints an empty element. */
0430: public void printEmptyElement(Element element) {
0431: printOpenElement(element, true);
0432: }
0433:
0434: /** Prints a close element. */
0435: public void printCloseElement(Element element) {
0436:
0437: out.print("</");
0438: out.print(element.getNodeName());
0439: out.print('>');
0440: out.flush();
0441:
0442: } // printCloseElement(Element)
0443:
0444: /** Prints an attribute. */
0445: public void printAttribute(Attr attribute) {
0446:
0447: String name = attribute.getNodeName();
0448: String value = attribute.getNodeValue();
0449:
0450: out.print(name);
0451: out.print('=');
0452: out.print('"');
0453: out.print(normalize(value));
0454: out.print('"');
0455:
0456: } // printAttribute(Attr)
0457:
0458: /** Prints text. */
0459: public void printText(Text text) {
0460: String value = text.getNodeValue();
0461: out.print(normalize(value));
0462: }
0463:
0464: // DTD printing methods
0465:
0466: /** Prints a DTD element declaration. */
0467: public void printElementDecl(Element element) {
0468:
0469: String elemName = element.getAttribute("name");
0470: Element model = XUtil
0471: .getFirstChildElement(element, "archetype");
0472:
0473: out.print("<!ELEMENT ");
0474: out.print(elemName);
0475: out.print(' ');
0476: printElementDeclContentModel(model);
0477: out.print('>');
0478: out.println();
0479: out.flush();
0480:
0481: Element archetype = XUtil.getFirstChildElement(element,
0482: "archetype");
0483: if (archetype != null) {
0484: Element attribute = XUtil.getFirstChildElement(archetype,
0485: "attribute");
0486: while (attribute != null) {
0487: printAttributeDecl(elemName, attribute);
0488: attribute = XUtil.getNextSiblingElement(attribute,
0489: "attribute");
0490: }
0491: }
0492:
0493: } // printElementDecl(Element)
0494:
0495: /** Prints a DTD element declaration content model. */
0496: public void printElementDeclContentModel(Element archetype) {
0497:
0498: String content = archetype.getAttribute("content");
0499: if (content.equals("empty") || content.equals("any")) {
0500: out.print(content.toUpperCase());
0501: } else if (content.equals("elemOnly")) {
0502: printElementDeclContentModelChildren(archetype);
0503: } else if (content.equals("mixed")
0504: || content.equals("textOnly")) {
0505: printElementDeclContentModelMixed(archetype);
0506: }
0507: out.flush();
0508:
0509: } // printElementDeclContentModel(Element)
0510:
0511: /** Prints a DTD element declaration mixed content model. */
0512: public void printElementDeclContentModelMixed(Element archetype) {
0513:
0514: Element element = XUtil.getFirstChildElement(archetype,
0515: "element");
0516: boolean textOnly = element == null;
0517: out.print("(#PCDATA");
0518: if (!textOnly) {
0519: while (element != null) {
0520: String elemName = element.getAttribute("ref");
0521: out.print('|');
0522: out.print(elemName);
0523: element = XUtil.getNextSiblingElement(element,
0524: "element");
0525: }
0526: }
0527: out.print(')');
0528: if (!textOnly) {
0529: out.print('*');
0530: }
0531:
0532: } // printElementDeclContentModelMixed(Element)
0533:
0534: /** Prints a DTD element declaration children content model. */
0535: public void printElementDeclContentModelChildren(Element archetype) {
0536:
0537: boolean simple = !containsMoreThanOneChildOfType(archetype,
0538: new String[] { "element", "group" })
0539: && XUtil.getFirstChildElement(archetype, "element") != null;
0540:
0541: if (simple) {
0542: out.print('(');
0543: }
0544: Element model = XUtil.getFirstChildElement(archetype,
0545: CONTENT_MODEL_ELEMENT_NAMES);
0546: while (model != null) {
0547: printElementDeclContentModelChildren0(model);
0548: model = XUtil.getNextSiblingElement(model,
0549: CONTENT_MODEL_ELEMENT_NAMES);
0550: if (model != null) {
0551: out.print(',');
0552: }
0553: }
0554: if (simple) {
0555: out.print(')');
0556: }
0557:
0558: } // printElementDeclContentModelChildren(Element)
0559:
0560: /** Prints a DTD attribute declaration. */
0561: public void printAttributeDecl(String elemName, Element attribute) {
0562:
0563: String attrName = attribute.getAttribute("name");
0564: String attrType = attribute.getAttribute("type");
0565: Node attrDefaultValueNode = attribute
0566: .getAttributeNode("default");
0567:
0568: out.print("<!ATTLIST ");
0569: out.print(elemName);
0570: out.print(' ');
0571: out.print(attrName);
0572: out.print(' ');
0573: if (isBasicAttributeType(attrType)) {
0574: Element enumeration = XUtil.getFirstChildElement(attribute,
0575: "enumeration");
0576: if (attrType.equals("NMTOKEN") && enumeration != null) {
0577: out.print('(');
0578: Element literal = XUtil.getFirstChildElement(
0579: enumeration, "literal");
0580: while (literal != null) {
0581: literal.normalize();
0582: Node literalValueNode = getFirstChildOfType(
0583: literal, Node.TEXT_NODE);
0584: String literalValue = literalValueNode != null ? literalValueNode
0585: .getNodeValue()
0586: : "";
0587: out.print(literalValue);
0588: literal = XUtil.getNextSiblingElement(literal,
0589: "literal");
0590: if (literal != null) {
0591: out.print('|');
0592: }
0593: }
0594: out.print(')');
0595: } else {
0596: out.print(attrType);
0597: }
0598: } else {
0599: out.print("CDATA");
0600: }
0601: if (attribute.getAttribute("minOccurs").equals("1")) {
0602: out.print(" #REQUIRED");
0603: } else if (attribute.getAttribute("fixed").length() > 0) {
0604: String attrFixedValue = attribute.getAttribute("fixed");
0605:
0606: out.print(" #FIXED ");
0607: out.print('"');
0608: out.print(normalize(attrFixedValue));
0609: out.print('"');
0610: } else if (attrDefaultValueNode == null) {
0611: out.print(" #IMPLIED");
0612: }
0613: if (attrDefaultValueNode != null) {
0614: String attrDefaultValue = attrDefaultValueNode
0615: .getNodeValue();
0616:
0617: out.print(' ');
0618: out.print('"');
0619: out.print(normalize(attrDefaultValue));
0620: out.print('"');
0621: }
0622: out.print('>');
0623: out.println();
0624: out.flush();
0625:
0626: } // printAttributeDecl(String,Element)
0627:
0628: /** Prints a DTD entity declaration. */
0629: public void printEntityDecl(Element entity) {
0630:
0631: String entityNodeName = entity.getNodeName();
0632: String entityName = entity.getAttribute("name");
0633:
0634: out.print("<!ENTITY ");
0635: out.print(entityName);
0636: out.print(' ');
0637:
0638: if (entityNodeName.equals("textEntity")) {
0639: entity.normalize();
0640: Node entityValueNode = getFirstChildOfType(entity,
0641: Node.TEXT_NODE);
0642: String entityValue = entityValueNode != null ? entityValueNode
0643: .getNodeValue()
0644: : "";
0645: out.print('"');
0646: out.print(normalize(entityValue));
0647: out.print('"');
0648: } else {
0649: String publicId = entity.getAttribute("public");
0650: String systemId = entity.getAttribute("system");
0651: if (publicId.length() > 0) {
0652: out.print("PUBLIC ");
0653: out.print('"');
0654: out.print(publicId);
0655: out.print('"');
0656: out.print(' ');
0657: out.print('"');
0658: out.print(systemId);
0659: out.print('"');
0660: } else if (systemId.length() > 0) {
0661: out.print("SYSTEM ");
0662: out.print('"');
0663: out.print(systemId);
0664: out.print('"');
0665: }
0666:
0667: if (entityNodeName.equals("unparsedEntity")) {
0668: String notationName = entity.getAttribute("notation");
0669: out.print(" NDATA ");
0670: out.print(notationName);
0671: }
0672: }
0673:
0674: out.print('>');
0675: out.println();
0676: out.flush();
0677:
0678: } // printEntityDecl(Element)
0679:
0680: /** Prints a DTD notation declaration. */
0681: public void printNotationDecl(Element notation) {
0682:
0683: String notationName = notation.getAttribute("name");
0684: String publicId = notation.getAttribute("public");
0685: String systemId = notation.getAttribute("system");
0686:
0687: out.print("<!NOTATION ");
0688: out.print(notationName);
0689: out.print(' ');
0690: if (publicId.length() > 0) {
0691: out.print("PUBLIC ");
0692: out.print('"');
0693: out.print(publicId);
0694: out.print('"');
0695: if (systemId.length() > 0) {
0696: out.print(' ');
0697: out.print('"');
0698: out.print(systemId);
0699: out.print('"');
0700: }
0701: } else if (systemId.length() > 0) {
0702: out.print("SYSTEM ");
0703: out.print('"');
0704: out.print(systemId);
0705: out.print('"');
0706: }
0707: out.print('>');
0708: out.println();
0709: out.flush();
0710:
0711: } // printNotationDecl(Element)
0712:
0713: //
0714: // Protected methods
0715: //
0716:
0717: /** Prints an open or empty element. */
0718: protected void printOpenElement(Element element, boolean empty) {
0719:
0720: out.print('<');
0721: out.print(element.getNodeName());
0722: NamedNodeMap attrs = element.getAttributes();
0723: int length = attrs.getLength();
0724: for (int i = 0; i < length; i++) {
0725: Attr attribute = (Attr) attrs.item(i);
0726: if (verbose || attribute.getSpecified()) {
0727: out.print(' ');
0728: printAttribute(attribute);
0729: }
0730: }
0731: if (empty) {
0732: out.print('/');
0733: }
0734: out.print('>');
0735: out.flush();
0736:
0737: } // printOpenElement(Element,boolean)
0738:
0739: /**
0740: * Returns true if the element is "empty". In other words, if it
0741: * does not contain element or text node children.
0742: */
0743: protected boolean isEmpty(Element element) {
0744: if (!element.hasChildNodes()) {
0745: return true;
0746: }
0747: Node child = element.getFirstChild();
0748: while (child != null) {
0749: int type = child.getNodeType();
0750: if (type == Node.ELEMENT_NODE || type == Node.TEXT_NODE) {
0751: return false;
0752: }
0753: child = child.getNextSibling();
0754: }
0755: return true;
0756: }
0757:
0758: /** Returns true if the attribute type is basic. */
0759: protected boolean isBasicAttributeType(String type) {
0760: return type.equals("ENTITY") || type.equals("ENTITIES")
0761: || type.equals("ID") || type.equals("IDREF")
0762: || type.equals("IDREFS") || type.equals("NMTOKEN")
0763: || type.equals("NMTOKENS");
0764: }
0765:
0766: /** Returns true if the occurrence count is basic. */
0767: protected boolean isBasicOccurrenceCount(String minOccurs,
0768: String maxOccurs) {
0769: int min = parseInt(minOccurs, 1);
0770: int max = parseInt(maxOccurs, 1);
0771: return (min == 0 && max == 1) || (min == 1 && max == 1)
0772: || (min == 0 && max == -1) || (min == 1 && max == -1);
0773: }
0774:
0775: /** Parses a string and returns the integer value. */
0776: protected int parseInt(String s, int defaultValue) {
0777: if (s == null || s.length() == 0) {
0778: return defaultValue;
0779: }
0780: try {
0781: return Integer.parseInt(s);
0782: } catch (NumberFormatException e) {
0783: // ignore
0784: }
0785: return -1;
0786: }
0787:
0788: /**
0789: * Returns true if the specified element has more than one child with
0790: * any of the given names.
0791: */
0792: protected boolean containsMoreThanOneChildOfType(Element node,
0793: String names[]) {
0794: int count = 0;
0795: Element child = XUtil.getFirstChildElement(node, names);
0796: while (child != null) {
0797: count++;
0798: child = XUtil.getNextSiblingElement(child, names);
0799: }
0800: return count > 1;
0801: }
0802:
0803: /** Returns the first child of the given node type. */
0804: protected Node getFirstChildOfType(Node node, short type) {
0805: if (node != null) {
0806: Node child = node.getFirstChild();
0807: while (child != null) {
0808: if (child.getNodeType() == type) {
0809: return child;
0810: }
0811: child = child.getNextSibling();
0812: }
0813: }
0814: return null;
0815: }
0816:
0817: /** Returns the next sibling of the given node type. */
0818: protected Node getNextSiblingOfType(Node node, short type) {
0819: if (node != null) {
0820: Node child = node.getNextSibling();
0821: while (child != null) {
0822: if (child.getNodeType() == type) {
0823: return child;
0824: }
0825: child = child.getNextSibling();
0826: }
0827: }
0828: return null;
0829: }
0830:
0831: /** Normalizes the given string. */
0832: protected String normalize(String s) {
0833: StringBuffer str = new StringBuffer();
0834:
0835: int len = (s != null) ? s.length() : 0;
0836: for (int i = 0; i < len; i++) {
0837: char ch = s.charAt(i);
0838: switch (ch) {
0839: case '<': {
0840: str.append("<");
0841: break;
0842: }
0843: case '>': {
0844: str.append(">");
0845: break;
0846: }
0847: case '&': {
0848: str.append("&");
0849: break;
0850: }
0851: case '"': {
0852: str.append(""");
0853: break;
0854: }
0855: /***
0856: case '\r':
0857: case '\n': {
0858: if (canonical) {
0859: str.append("&#");
0860: str.append(Integer.toString(ch));
0861: str.append(';');
0862: break;
0863: }
0864: // else, default append char
0865: }
0866: /***/
0867: default: {
0868: str.append(ch);
0869: }
0870: }
0871: }
0872:
0873: return str.toString();
0874:
0875: } // normalize(String):String
0876:
0877: //
0878: // Private methods
0879: //
0880:
0881: /** Initialize data. */
0882: private void init() {
0883:
0884: indent = 0;
0885: verbose = false;
0886: format = OutputFormat.XML_SCHEMA;
0887: encoding = "UTF8";
0888: canonical = false;
0889:
0890: } // init()
0891:
0892: /** Prints a DTD element declaration children content model. */
0893: private void printElementDeclContentModelChildren0(Element model) {
0894:
0895: String modelNodeName = model.getNodeName();
0896: if (modelNodeName.equals("element")) {
0897: String s = buildOccurrenceCountString(model
0898: .getAttribute("ref"), model
0899: .getAttribute("minOccurs"), model
0900: .getAttribute("maxOccurs"));
0901: out.print(s);
0902: } else {
0903: char separator = ',';
0904: String order = model.getAttribute("order");
0905: if (order.equals("choice")) {
0906: separator = '|';
0907: } else if (order.equals("all")) {
0908: separator = '&';
0909: }
0910:
0911: // swap out writer to capture this
0912: StringWriter writer = new StringWriter();
0913: PrintWriter printer = new PrintWriter(writer);
0914: PrintWriter oprinter = out;
0915: out = printer;
0916:
0917: // build model
0918: out.print('(');
0919: Element child = XUtil.getFirstChildElement(model,
0920: CONTENT_MODEL_ELEMENT_NAMES);
0921: while (child != null) {
0922: printElementDeclContentModelChildren0(child);
0923: child = XUtil.getNextSiblingElement(child,
0924: CONTENT_MODEL_ELEMENT_NAMES);
0925: if (child != null) {
0926: out.print(separator);
0927: }
0928: }
0929: out.print(')');
0930:
0931: // handle all case
0932: String output = writer.toString();
0933: if (separator == '&') {
0934: if (output.startsWith("(") && output.endsWith(")")) {
0935: output = output.substring(1, output.length() - 1);
0936: }
0937: output = expandAllModel(output);
0938: }
0939:
0940: // build occurrent count string
0941: output = buildOccurrenceCountString(output, model
0942: .getAttribute("minOccurs"), model
0943: .getAttribute("maxOccurs"));
0944:
0945: // change the writer back and output model
0946: out = oprinter;
0947: out.print(output);
0948: }
0949:
0950: } // printElementDeclContentModelChildren0(Element)
0951:
0952: /** Expands the all content model. */
0953: private String expandAllModel(String model) {
0954:
0955: // get pieces
0956: Vector piecesVector = new Vector();
0957: StringTokenizer tokenizer = new StringTokenizer(model, "&");
0958: while (tokenizer.hasMoreTokens()) {
0959: String piece = tokenizer.nextToken();
0960: piecesVector.addElement(piece);
0961: }
0962:
0963: // expand all content model
0964: int length = piecesVector.size();
0965: if (length > 1) {
0966: String pieces[] = new String[length];
0967: for (int i = 0; i < pieces.length; i++) {
0968: pieces[i] = (String) piecesVector.elementAt(i);
0969: }
0970: String allModel = "(" + buildAllModel(pieces, 0) + ')';
0971: return allModel;
0972: }
0973:
0974: return model;
0975:
0976: } // expandAllModel(String):String
0977:
0978: /** Builds the all content model. */
0979: private String buildAllModel(String src[], int offset) {
0980:
0981: // swap last two places
0982: if (src.length - offset == 2) {
0983: StringBuffer str = new StringBuffer();
0984: str.append(createSeq(src));
0985: swap(src, offset, offset + 1);
0986: str.append('|');
0987: str.append(createSeq(src));
0988: swap(src, offset, offset + 1);
0989: return str.toString();
0990: }
0991:
0992: // recurse
0993: String copy[] = new String[src.length];
0994: StringBuffer str = new StringBuffer();
0995: for (int i = offset; i < src.length; i++) {
0996: System.arraycopy(src, 0, copy, 0, src.length);
0997: shift(copy, offset, i);
0998: str.append(buildAllModel(copy, offset + 1));
0999: if (i < src.length - 1) {
1000: str.append('|');
1001: }
1002: }
1003:
1004: return str.toString();
1005:
1006: } // buildAllModel(String[],int):String
1007:
1008: /** Creates an all content model sequence string. */
1009: private String createSeq(String src[]) {
1010:
1011: StringBuffer str = new StringBuffer();
1012: str.append('(');
1013: for (int i = 0; i < src.length; i++) {
1014: str.append(src[i]);
1015: if (i < src.length - 1) {
1016: str.append(',');
1017: }
1018: }
1019: str.append(')');
1020:
1021: return str.toString();
1022:
1023: } // createSeq(String[]):String
1024:
1025: /** Shifts a value into position. */
1026: private void shift(String src[], int pos, int offset) {
1027:
1028: String temp = src[offset];
1029: for (int i = offset; i > pos; i--) {
1030: src[i] = src[i - 1];
1031: }
1032: src[pos] = temp;
1033:
1034: } // shift(String[],int,int)
1035:
1036: /** Swaps two values. */
1037: private void swap(String src[], int i, int j) {
1038:
1039: String temp = src[i];
1040: src[i] = src[j];
1041: src[j] = temp;
1042:
1043: } // swap(String[],int,int)
1044:
1045: /** Builds the DTD occurrent count string. */
1046: private String buildOccurrenceCountString(String model,
1047: String minOccurs, String maxOccurs) {
1048:
1049: // figure out min/max and if this range is bounded
1050: int min = parseInt(minOccurs, 0);
1051: int max = parseInt(maxOccurs, 1);
1052: boolean bounded = true;
1053: if (max == -1) {
1054: max = min;
1055: bounded = false;
1056: }
1057:
1058: // build string
1059: StringBuffer str = new StringBuffer();
1060: if (min == 0 && max == 1 && bounded) {
1061: str.append(model);
1062: str.append('?');
1063: } else if (min == 0 && max == 0 && !bounded) {
1064: str.append(model);
1065: str.append('*');
1066: } else if (min == 1 && max == 1 && !bounded) {
1067: str.append(model);
1068: str.append('+');
1069: } else if (min == 1 && max == 1 && bounded) {
1070: str.append(model);
1071: } else {
1072: str.append('(');
1073: for (int i = 0; i < min; i++) {
1074: str.append(model);
1075: if (i < min - 1) {
1076: str.append(',');
1077: }
1078: }
1079: if (max > min) {
1080: for (int i = min; i < max; i++) {
1081: str.append(',');
1082: str.append(model);
1083: str.append('?');
1084: }
1085: }
1086: if (!bounded) {
1087: str.append(',');
1088: str.append(model);
1089: str.append('*');
1090: }
1091: str.append(')');
1092: }
1093:
1094: // return
1095: return str.toString();
1096:
1097: } // buildOccurrenceCountString(String,String,String):String
1098:
1099: //
1100: // Classes
1101: //
1102:
1103: /**
1104: * Output format enumeration.
1105: */
1106: public static final class OutputFormat {
1107:
1108: //
1109: // Constants
1110: //
1111:
1112: /** Output format: DTD. */
1113: public static final OutputFormat DTD = new OutputFormat(0);
1114:
1115: /** Output format: XML Schema. */
1116: public static final OutputFormat XML_SCHEMA = new OutputFormat(
1117: 1);
1118:
1119: //
1120: // Data
1121: //
1122:
1123: /** Value. */
1124: private int value;
1125:
1126: //
1127: // Constructors
1128: //
1129:
1130: /** This class can't be constructed by anyone else. */
1131: private OutputFormat(int value) {
1132: this .value = value;
1133: }
1134:
1135: //
1136: // Public methods
1137: //
1138:
1139: /** Returns the value. */
1140: public int getValue() {
1141: return value;
1142: }
1143:
1144: //
1145: // Object methods
1146: //
1147:
1148: /** Returns the hash code. */
1149: public int hashCode() {
1150: return value;
1151: }
1152:
1153: /** Returns true if the objects are equal. */
1154: public boolean equals(Object object) {
1155: if (object == null || getClass() != object.getClass()) {
1156: return false;
1157: }
1158: return value == ((OutputFormat) object).getValue();
1159: }
1160:
1161: /** Returns a string representation of this object. */
1162: public String toString() {
1163: if (this == DTD) {
1164: return "DTD";
1165: }
1166: if (this == XML_SCHEMA) {
1167: return "XML SCHEMA";
1168: }
1169: return "???";
1170: }
1171:
1172: } // class OutputFormat
1173:
1174: } // class XGrammarWriter
|