001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.internal.xjc.reader.dtd;
027:
028: import java.io.IOException;
029: import java.util.Collections;
030: import java.util.HashMap;
031: import java.util.Map;
032: import java.util.Stack;
033:
034: import javax.xml.namespace.QName;
035:
036: import com.sun.codemodel.internal.JClass;
037: import com.sun.codemodel.internal.JCodeModel;
038: import com.sun.codemodel.internal.JDefinedClass;
039: import com.sun.codemodel.internal.JPackage;
040: import com.sun.tools.internal.xjc.AbortException;
041: import com.sun.tools.internal.xjc.ErrorReceiver;
042: import com.sun.tools.internal.xjc.Options;
043: import com.sun.tools.internal.xjc.model.CAttributePropertyInfo;
044: import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo;
045: import com.sun.tools.internal.xjc.model.CClassInfo;
046: import com.sun.tools.internal.xjc.model.CPropertyInfo;
047: import com.sun.tools.internal.xjc.model.Model;
048: import com.sun.tools.internal.xjc.model.TypeUse;
049: import com.sun.tools.internal.xjc.model.TypeUseFactory;
050: import com.sun.tools.internal.xjc.model.CDefaultValue;
051: import com.sun.tools.internal.xjc.reader.ModelChecker;
052: import com.sun.tools.internal.xjc.reader.Ring;
053: import com.sun.tools.internal.xjc.reader.dtd.bindinfo.BIAttribute;
054: import com.sun.tools.internal.xjc.reader.dtd.bindinfo.BIElement;
055: import com.sun.tools.internal.xjc.reader.dtd.bindinfo.BIInterface;
056: import com.sun.tools.internal.xjc.reader.dtd.bindinfo.BindInfo;
057: import com.sun.tools.internal.xjc.util.CodeModelClassFactory;
058: import com.sun.tools.internal.xjc.util.ErrorReceiverFilter;
059: import com.sun.xml.internal.bind.api.impl.NameConverter;
060: import com.sun.xml.internal.dtdparser.DTDHandlerBase;
061: import com.sun.xml.internal.dtdparser.DTDParser;
062: import com.sun.xml.internal.dtdparser.InputEntity;
063: import com.sun.xml.internal.xsom.XmlString;
064: import com.sun.istack.internal.SAXParseException2;
065:
066: import org.xml.sax.EntityResolver;
067: import org.xml.sax.InputSource;
068: import org.xml.sax.Locator;
069: import org.xml.sax.SAXException;
070: import org.xml.sax.SAXParseException;
071: import org.xml.sax.helpers.LocatorImpl;
072:
073: /**
074: * Parses DTD grammar along with binding information into BGM.
075: *
076: * @author
077: * <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a>
078: */
079: public class TDTDReader extends DTDHandlerBase {
080: /**
081: * Parses DTD grammar and a binding information into BGM.
082: *
083: * <p>
084: * This method is just a utility method that covers 80% of the use
085: * cases.
086: *
087: * @param bindingInfo
088: * binding information file, if any. Can be null.
089: */
090: public static Model parse(InputSource dtd, InputSource bindingInfo,
091: ErrorReceiver errorReceiver, Options opts) {
092:
093: try {
094: // set up a ring
095: final Ring old = Ring.begin();
096: try {
097: ErrorReceiverFilter ef = new ErrorReceiverFilter(
098: errorReceiver);
099:
100: JCodeModel cm = new JCodeModel();
101: Model model = new Model(opts, cm,
102: NameConverter.standard, opts.classNameAllocator);
103:
104: Ring.add(cm);
105: Ring.add(model);
106: Ring.add(ErrorReceiver.class, ef);
107:
108: TDTDReader reader = new TDTDReader(ef,
109: opts.entityResolver, opts, bindingInfo);
110:
111: DTDParser parser = new DTDParser();
112: parser.setDtdHandler(reader);
113: if (opts.entityResolver != null)
114: parser.setEntityResolver(opts.entityResolver);
115:
116: try {
117: parser.parse(dtd);
118: } catch (SAXParseException e) {
119: return null; // this error was already handled by GrammarReaderController
120: }
121:
122: Ring.get(ModelChecker.class).check();
123:
124: if (ef.hadError())
125: return null;
126: else
127: return model;
128: } finally {
129: Ring.end(old);
130: }
131: } catch (IOException e) {
132: errorReceiver.error(new SAXParseException2(e.getMessage(),
133: null, e));
134: return null;
135: } catch (SAXException e) {
136: errorReceiver.error(new SAXParseException2(e.getMessage(),
137: null, e));
138: return null;
139: } catch (AbortException e) {
140: // parsing was aborted but the error was already reported
141: return null;
142: }
143: }
144:
145: protected TDTDReader(ErrorReceiver errorReceiver,
146: EntityResolver entityResolver, Options opts,
147: InputSource _bindInfo) throws AbortException {
148: this .entityResolver = entityResolver;
149: this .errorReceiver = new ErrorReceiverFilter(errorReceiver);
150: this .opts = opts;
151: bindInfo = new BindInfo(model, _bindInfo, this .errorReceiver);
152: classFactory = new CodeModelClassFactory(errorReceiver);
153: }
154:
155: private final EntityResolver entityResolver;
156:
157: private final Options opts;
158:
159: /**
160: * binding information.
161: *
162: * <p>
163: * This is always non-null even if no binding information was specified.
164: * (In that case, a dummy object will be provided.)
165: */
166: final BindInfo bindInfo;
167:
168: private final JCodeModel codeModel = Ring.get(JCodeModel.class);
169:
170: final Model model = Ring.get(Model.class);
171:
172: private final CodeModelClassFactory classFactory;
173:
174: private final ErrorReceiverFilter errorReceiver;
175:
176: /**
177: * Element name to its content model definition.
178: */
179: private final Map<String, Element> elements = new HashMap<String, Element>();
180:
181: public void startDTD(InputEntity entity) throws SAXException {
182: }
183:
184: public void endDTD() throws SAXException {
185:
186: // bind them all.
187: // we need to know how elements are referencing each other before we do this,
188: // so this can be only done at the endDTD method
189: for (Element e : elements.values())
190: e.bind();
191:
192: // if there was an error by now, just abort.
193: if (errorReceiver.hadError())
194: return;
195:
196: processInterfaceDeclarations();
197:
198: // check XJC extensions and realize them
199: model.serialVersionUID = bindInfo.getSerialVersionUID();
200: model.rootClass = bindInfo.getSuperClass();
201: model.rootInterface = bindInfo.getSuperInterface();
202:
203: // TODO: do we need to reimplement them?
204: // // performs annotation
205: // Annotator.annotate(model, this);
206: // FieldCollisionChecker.check( model, this );
207:
208: processConstructorDeclarations();
209: }
210:
211: /** Processes interface declarations. */
212: private void processInterfaceDeclarations() {
213:
214: Map<String, InterfaceAcceptor> fromName = new HashMap<String, InterfaceAcceptor>();
215:
216: // first, create empty InterfaceItem declaration for all interfaces
217: Map<BIInterface, JClass> decls = new HashMap<BIInterface, JClass>();
218:
219: for (BIInterface decl : bindInfo.interfaces()) {
220: final JDefinedClass intf = classFactory.createInterface(
221: getTargetPackage(), decl.name(), copyLocator());
222: decls.put(decl, intf);
223: fromName.put(decl.name(), new InterfaceAcceptor() {
224: public void implement(JClass c) {
225: intf._implements (c);
226: }
227: });
228: }
229:
230: for (final CClassInfo ci : model.beans().values()) {
231: fromName.put(ci.getName(), new InterfaceAcceptor() {
232: public void implement(JClass c) {
233: ci._implements (c);
234: }
235: });
236: }
237:
238: // traverse the interface declarations again
239: // and populate its expression according to the members attribute.
240: for (Map.Entry<BIInterface, JClass> e : decls.entrySet()) {
241: BIInterface decl = e.getKey();
242: JClass c = e.getValue();
243:
244: for (String member : decl.members()) {
245: InterfaceAcceptor acc = fromName.get(member);
246: if (acc == null) {
247: // there is no such class/interface
248: // TODO: error location
249: error(
250: decl.getSourceLocation(),
251: Messages.ERR_BINDINFO_NON_EXISTENT_INTERFACE_MEMBER,
252: member);
253: continue;
254: }
255:
256: acc.implement(c);
257: }
258: }
259:
260: // TODO: check the cyclic interface definition
261: }
262:
263: private static interface InterfaceAcceptor {
264: void implement(JClass c);
265: }
266:
267: JPackage getTargetPackage() {
268: // "-p" takes precedence over everything else
269: if (opts.defaultPackage != null)
270: return codeModel._package(opts.defaultPackage);
271: else
272: return bindInfo.getTargetPackage();
273: }
274:
275: /**
276: * Creates constructor declarations as specified in the
277: * binding information.
278: *
279: * <p>
280: * Also checks that the binding file does not contain
281: * declarations for non-existent elements.
282: */
283: private void processConstructorDeclarations() {
284: for (BIElement decl : bindInfo.elements()) {
285: Element e = elements.get(decl.name());
286: if (e == null) {
287: error(
288: decl.getSourceLocation(),
289: Messages.ERR_BINDINFO_NON_EXISTENT_ELEMENT_DECLARATION,
290: decl.name());
291: continue; // continue to process next declaration
292: }
293:
294: if (!decl.isClass())
295: // only element-class declaration has constructor definitions
296: continue;
297:
298: decl.declareConstructors(e.getClassInfo());
299: }
300: }
301:
302: public void attributeDecl(String elementName, String attributeName,
303: String attributeType, String[] enumeration,
304: short attributeUse, String defaultValue)
305: throws SAXException {
306: getOrCreateElement(elementName).attributes.add(createAttribute(
307: elementName, attributeName, attributeType, enumeration,
308: attributeUse, defaultValue));
309: }
310:
311: protected CPropertyInfo createAttribute(String elementName,
312: String attributeName, String attributeType, String[] enums,
313: short attributeUse, String defaultValue)
314: throws SAXException {
315:
316: boolean required = attributeUse == USE_REQUIRED;
317:
318: // get the attribute-property declaration
319: BIElement edecl = bindInfo.element(elementName);
320: BIAttribute decl = null;
321: if (edecl != null)
322: decl = edecl.attribute(attributeName);
323:
324: String propName;
325: if (decl == null)
326: propName = model.getNameConverter().toPropertyName(
327: attributeName);
328: else
329: propName = decl.getPropertyName();
330:
331: QName qname = new QName("", attributeName);
332:
333: // if no declaration is specified, just wrap it by
334: // a FieldItem and let the normalizer handle its content.
335: TypeUse use;
336:
337: if (decl != null && decl.getConversion() != null)
338: use = decl.getConversion().getTransducer();
339: else
340: use = builtinConversions.get(attributeType);
341:
342: CPropertyInfo r = new CAttributePropertyInfo(propName, null,
343: null/*TODO*/, copyLocator(), qname, use, required);
344:
345: if (defaultValue != null)
346: r.defaultValue = CDefaultValue.create(use, new XmlString(
347: defaultValue));
348:
349: return r;
350: }
351:
352: Element getOrCreateElement(String elementName) {
353: Element r = elements.get(elementName);
354: if (r == null) {
355: r = new Element(this , elementName);
356: elements.put(elementName, r);
357: }
358:
359: return r;
360: }
361:
362: public void startContentModel(String elementName,
363: short contentModelType) throws SAXException {
364: assert modelGroups.isEmpty();
365: modelGroups.push(new ModelGroup());
366: }
367:
368: public void endContentModel(String elementName,
369: short contentModelType) throws SAXException {
370: assert modelGroups.size() == 1;
371: Term term = modelGroups.pop().wrapUp();
372:
373: Element e = getOrCreateElement(elementName);
374: e.define(contentModelType, term, copyLocator());
375: }
376:
377: private final Stack<ModelGroup> modelGroups = new Stack<ModelGroup>();
378:
379: public void startModelGroup() throws SAXException {
380: modelGroups.push(new ModelGroup());
381: }
382:
383: public void endModelGroup(short occurence) throws SAXException {
384: Term t = Occurence.wrap(modelGroups.pop().wrapUp(), occurence);
385: modelGroups.peek().addTerm(t);
386: }
387:
388: public void connector(short connectorType) throws SAXException {
389: modelGroups.peek().setKind(connectorType);
390: }
391:
392: // TODO: for now, we just ignore all the content model specification
393: // and treat it as (A,B,C,....)
394:
395: public void childElement(String elementName, short occurence)
396: throws SAXException {
397: Element child = getOrCreateElement(elementName);
398: modelGroups.peek().addTerm(Occurence.wrap(child, occurence));
399: child.isReferenced = true;
400: }
401:
402: /**
403: * Mutable {@link Locator} instance that points to
404: * the current source line.
405: * <p>
406: * Use {@link #copyLocator()} to get a immutable clone.
407: */
408: private Locator locator;
409:
410: public void setDocumentLocator(Locator loc) {
411: this .locator = loc;
412: }
413:
414: /**
415: * Creates a snapshot of the current {@link #locator} values.
416: */
417: private Locator copyLocator() {
418: return new LocatorImpl(locator);
419: }
420:
421: //
422: //
423: // builtin datatype handling
424: //
425: //
426:
427: /** Transducers for the built-in types. Read-only. */
428: private static final Map<String, TypeUse> builtinConversions;
429:
430: static {
431: // list of datatypes which have built-in conversions.
432: // note that although xs:token and xs:normalizedString are not
433: // specified in the spec, they need to be here because they
434: // have different whitespace normalization semantics.
435: Map<String, TypeUse> m = new HashMap<String, TypeUse>();
436:
437: m.put("CDATA", CBuiltinLeafInfo.NORMALIZED_STRING);
438: m.put("ENTITY", CBuiltinLeafInfo.TOKEN);
439: m.put("ENTITIES", CBuiltinLeafInfo.STRING.makeCollection());
440: m.put("NMTOKEN", CBuiltinLeafInfo.TOKEN);
441: m.put("NMTOKENS", CBuiltinLeafInfo.STRING.makeCollection());
442: m.put("ID", CBuiltinLeafInfo.ID);
443: m.put("IDREF", CBuiltinLeafInfo.IDREF);
444: m.put("IDREFS", TypeUseFactory
445: .makeCollection(CBuiltinLeafInfo.IDREF));
446: m.put("ENUMERATION", CBuiltinLeafInfo.TOKEN);
447:
448: builtinConversions = Collections.unmodifiableMap(m);
449: }
450:
451: //
452: //
453: // error related utility methods
454: //
455: //
456: public void error(SAXParseException e) throws SAXException {
457: errorReceiver.error(e);
458: }
459:
460: public void fatalError(SAXParseException e) throws SAXException {
461: errorReceiver.fatalError(e);
462: }
463:
464: public void warning(SAXParseException e) throws SAXException {
465: errorReceiver.warning(e);
466: }
467:
468: protected final void error(Locator loc, String prop, Object... args) {
469: errorReceiver.error(loc, Messages.format(prop, args));
470: }
471:
472: }
|