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