/*
 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.tools.internal.xjc.reader.dtd;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import javax.xml.namespace.QName;

import com.sun.codemodel.internal.JClass;
import com.sun.codemodel.internal.JCodeModel;
import com.sun.codemodel.internal.JDefinedClass;
import com.sun.codemodel.internal.JPackage;
import com.sun.tools.internal.xjc.AbortException;
import com.sun.tools.internal.xjc.ErrorReceiver;
import com.sun.tools.internal.xjc.Options;
import com.sun.tools.internal.xjc.model.CAttributePropertyInfo;
import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo;
import com.sun.tools.internal.xjc.model.CClassInfo;
import com.sun.tools.internal.xjc.model.CPropertyInfo;
import com.sun.tools.internal.xjc.model.Model;
import com.sun.tools.internal.xjc.model.TypeUse;
import com.sun.tools.internal.xjc.model.TypeUseFactory;
import com.sun.tools.internal.xjc.model.CDefaultValue;
import com.sun.tools.internal.xjc.reader.ModelChecker;
import com.sun.tools.internal.xjc.reader.Ring;
import com.sun.tools.internal.xjc.reader.dtd.bindinfo.BIAttribute;
import com.sun.tools.internal.xjc.reader.dtd.bindinfo.BIElement;
import com.sun.tools.internal.xjc.reader.dtd.bindinfo.BIInterface;
import com.sun.tools.internal.xjc.reader.dtd.bindinfo.BindInfo;
import com.sun.tools.internal.xjc.util.CodeModelClassFactory;
import com.sun.tools.internal.xjc.util.ErrorReceiverFilter;
import com.sun.xml.internal.bind.api.impl.NameConverter;
import com.sun.xml.internal.dtdparser.DTDHandlerBase;
import com.sun.xml.internal.dtdparser.DTDParser;
import com.sun.xml.internal.dtdparser.InputEntity;
import com.sun.xml.internal.xsom.XmlString;
import com.sun.istack.internal.SAXParseException2;

import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.LocatorImpl;

Parses DTD grammar along with binding information into BGM.
Author: Kohsuke KAWAGUCHI
/** * Parses DTD grammar along with binding information into BGM. * * @author * <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a> */
public class TDTDReader extends DTDHandlerBase {
Parses DTD grammar and a binding information into BGM.

This method is just a utility method that covers 80% of the use cases.

Params:
  • bindingInfo – binding information file, if any. Can be null.
/** * Parses DTD grammar and a binding information into BGM. * * <p> * This method is just a utility method that covers 80% of the use * cases. * * @param bindingInfo * binding information file, if any. Can be null. */
public static Model parse( InputSource dtd, InputSource bindingInfo, ErrorReceiver errorReceiver, Options opts) { try { // set up a ring final Ring old = Ring.begin(); try { ErrorReceiverFilter ef = new ErrorReceiverFilter(errorReceiver); JCodeModel cm = new JCodeModel(); Model model = new Model(opts,cm,NameConverter.standard,opts.classNameAllocator,null); Ring.add(cm); Ring.add(model); Ring.add(ErrorReceiver.class,ef); TDTDReader reader = new TDTDReader( ef, opts, bindingInfo); DTDParser parser = new DTDParser(); parser.setDtdHandler(reader); if( opts.entityResolver!=null ) parser.setEntityResolver(opts.entityResolver); try { parser.parse(dtd); } catch (SAXParseException e) { return null; // this error was already handled by GrammarReaderController } Ring.get(ModelChecker.class).check(); if(ef.hadError()) return null; else return model; } finally { Ring.end(old); } } catch (IOException e) { errorReceiver.error(new SAXParseException2(e.getMessage(),null,e)); return null; } catch (SAXException e) { errorReceiver.error(new SAXParseException2(e.getMessage(),null,e)); return null; } catch (AbortException e) { // parsing was aborted but the error was already reported return null; } } protected TDTDReader(ErrorReceiver errorReceiver, Options opts, InputSource _bindInfo) throws AbortException { this.entityResolver = opts.entityResolver; this.errorReceiver = new ErrorReceiverFilter(errorReceiver); bindInfo = new BindInfo(model,_bindInfo, this.errorReceiver); classFactory = new CodeModelClassFactory(errorReceiver); } private final EntityResolver entityResolver;
binding information.

This is always non-null even if no binding information was specified. (In that case, a dummy object will be provided.)

/** * binding information. * * <p> * This is always non-null even if no binding information was specified. * (In that case, a dummy object will be provided.) */
final BindInfo bindInfo; final Model model = Ring.get(Model.class); private final CodeModelClassFactory classFactory; private final ErrorReceiverFilter errorReceiver;
Element name to its content model definition.
/** * Element name to its content model definition. */
private final Map<String,Element> elements = new HashMap<String,Element>(); public void startDTD(InputEntity entity) throws SAXException { } public void endDTD() throws SAXException { // bind them all. // we need to know how elements are referencing each other before we do this, // so this can be only done at the endDTD method for( Element e : elements.values() ) e.bind(); // if there was an error by now, just abort. if (errorReceiver.hadError()) return; processInterfaceDeclarations(); // check XJC extensions and realize them model.serialVersionUID = bindInfo.getSerialVersionUID(); if(model.serialVersionUID!=null) model.serializable=true; model.rootClass = bindInfo.getSuperClass(); model.rootInterface = bindInfo.getSuperInterface(); // TODO: do we need to reimplement them? // // performs annotation // Annotator.annotate(model, this); // FieldCollisionChecker.check( model, this ); processConstructorDeclarations(); }
Processes interface declarations.
/** Processes interface declarations. */
private void processInterfaceDeclarations() { Map<String,InterfaceAcceptor> fromName = new HashMap<String,InterfaceAcceptor>(); // first, create empty InterfaceItem declaration for all interfaces Map<BIInterface,JClass> decls = new HashMap<BIInterface,JClass>(); for( BIInterface decl : bindInfo.interfaces() ) { final JDefinedClass intf = classFactory.createInterface( bindInfo.getTargetPackage(), decl.name(), copyLocator() ); decls.put(decl,intf); fromName.put(decl.name(),new InterfaceAcceptor() { public void implement(JClass c) { intf._implements(c); } }); } for( final CClassInfo ci : model.beans().values() ) { fromName.put(ci.getName(),new InterfaceAcceptor() { public void implement(JClass c) { ci._implements(c); } }); } // traverse the interface declarations again // and populate its expression according to the members attribute. for( Map.Entry<BIInterface,JClass> e : decls.entrySet() ) { BIInterface decl = e.getKey(); JClass c = e.getValue(); for (String member : decl.members()) { InterfaceAcceptor acc = fromName.get(member); if (acc == null) { // there is no such class/interface // TODO: error location error(decl.getSourceLocation(), Messages.ERR_BINDINFO_NON_EXISTENT_INTERFACE_MEMBER, member); continue; } acc.implement(c); } } // TODO: check the cyclic interface definition } private static interface InterfaceAcceptor { void implement( JClass c ); } JPackage getTargetPackage() { return bindInfo.getTargetPackage(); }
Creates constructor declarations as specified in the binding information.

Also checks that the binding file does not contain declarations for non-existent elements.

/** * Creates constructor declarations as specified in the * binding information. * * <p> * Also checks that the binding file does not contain * declarations for non-existent elements. */
private void processConstructorDeclarations() { for( BIElement decl: bindInfo.elements() ) { Element e = elements.get(decl.name()); if(e==null) { error(decl.getSourceLocation(), Messages.ERR_BINDINFO_NON_EXISTENT_ELEMENT_DECLARATION,decl.name()); continue; // continue to process next declaration } if(!decl.isClass()) // only element-class declaration has constructor definitions continue; decl.declareConstructors(e.getClassInfo()); } } public void attributeDecl(String elementName, String attributeName, String attributeType, String[] enumeration, short attributeUse, String defaultValue) throws SAXException { getOrCreateElement(elementName).attributes.add( createAttribute(elementName, attributeName, attributeType, enumeration, attributeUse, defaultValue) ); } protected CPropertyInfo createAttribute( String elementName, String attributeName, String attributeType, String[] enums, short attributeUse, String defaultValue ) throws SAXException { boolean required = attributeUse==USE_REQUIRED; // get the attribute-property declaration BIElement edecl = bindInfo.element(elementName); BIAttribute decl=null; if(edecl!=null) decl=edecl.attribute(attributeName); String propName; if(decl==null) propName = model.getNameConverter().toPropertyName(attributeName); else propName = decl.getPropertyName(); QName qname = new QName("",attributeName); // if no declaration is specified, just wrap it by // a FieldItem and let the normalizer handle its content. TypeUse use; if(decl!=null && decl.getConversion()!=null) use = decl.getConversion().getTransducer(); else use = builtinConversions.get(attributeType); CPropertyInfo r = new CAttributePropertyInfo( propName, null,null/*TODO*/, copyLocator(), qname, use, null, required ); if(defaultValue!=null) r.defaultValue = CDefaultValue.create( use, new XmlString(defaultValue) ); return r; } Element getOrCreateElement( String elementName ) { Element r = elements.get(elementName); if(r==null) { r = new Element(this,elementName); elements.put(elementName,r); } return r; } public void startContentModel(String elementName, short contentModelType) throws SAXException { assert modelGroups.isEmpty(); modelGroups.push(new ModelGroup()); } public void endContentModel(String elementName, short contentModelType) throws SAXException { assert modelGroups.size()==1; Term term = modelGroups.pop().wrapUp(); Element e = getOrCreateElement(elementName); e.define( contentModelType, term, copyLocator() ); } private final Stack<ModelGroup> modelGroups = new Stack<ModelGroup>(); public void startModelGroup() throws SAXException { modelGroups.push(new ModelGroup()); } public void endModelGroup(short occurence) throws SAXException { Term t = Occurence.wrap( modelGroups.pop().wrapUp(), occurence ); modelGroups.peek().addTerm(t); } public void connector(short connectorType) throws SAXException { modelGroups.peek().setKind(connectorType); } // TODO: for now, we just ignore all the content model specification // and treat it as (A,B,C,....) public void childElement(String elementName, short occurence) throws SAXException { Element child = getOrCreateElement(elementName); modelGroups.peek().addTerm( Occurence.wrap( child, occurence ) ); child.isReferenced = true; }
Mutable Locator instance that points to the current source line.

Use copyLocator() to get a immutable clone.

/** * Mutable {@link Locator} instance that points to * the current source line. * <p> * Use {@link #copyLocator()} to get a immutable clone. */
private Locator locator; public void setDocumentLocator(Locator loc) { this.locator = loc; }
Creates a snapshot of the current locator values.
/** * Creates a snapshot of the current {@link #locator} values. */
private Locator copyLocator(){ return new LocatorImpl(locator); } // // // builtin datatype handling // //
Transducers for the built-in types. Read-only.
/** Transducers for the built-in types. Read-only. */
private static final Map<String,TypeUse> builtinConversions; static { // list of datatypes which have built-in conversions. // note that although xs:token and xs:normalizedString are not // specified in the spec, they need to be here because they // have different whitespace normalization semantics. Map<String,TypeUse> m = new HashMap<String,TypeUse>(); m.put("CDATA", CBuiltinLeafInfo.NORMALIZED_STRING); m.put("ENTITY", CBuiltinLeafInfo.TOKEN); m.put("ENTITIES", CBuiltinLeafInfo.STRING.makeCollection()); m.put("NMTOKEN", CBuiltinLeafInfo.TOKEN); m.put("NMTOKENS", CBuiltinLeafInfo.STRING.makeCollection()); m.put("ID", CBuiltinLeafInfo.ID); m.put("IDREF", CBuiltinLeafInfo.IDREF); m.put("IDREFS", TypeUseFactory.makeCollection(CBuiltinLeafInfo.IDREF)); m.put("ENUMERATION",CBuiltinLeafInfo.TOKEN); builtinConversions = Collections.unmodifiableMap(m); } // // // error related utility methods // // public void error(SAXParseException e) throws SAXException { errorReceiver.error(e); } public void fatalError(SAXParseException e) throws SAXException { errorReceiver.fatalError(e); } public void warning(SAXParseException e) throws SAXException { errorReceiver.warning(e); } protected final void error( Locator loc, String prop, Object... args ) { errorReceiver.error(loc,Messages.format(prop,args)); } }