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:
037: package com.sun.tools.xjc.reader.dtd;
038:
039: import java.util.ArrayList;
040: import java.util.HashSet;
041: import java.util.List;
042: import java.util.Set;
043:
044: import javax.xml.namespace.QName;
045:
046: import com.sun.tools.xjc.model.CBuiltinLeafInfo;
047: import com.sun.tools.xjc.model.CClassInfo;
048: import com.sun.tools.xjc.model.CElementPropertyInfo;
049: import static com.sun.tools.xjc.model.CElementPropertyInfo.CollectionMode.*;
050: import com.sun.tools.xjc.model.CPropertyInfo;
051: import com.sun.tools.xjc.model.CReferencePropertyInfo;
052: import com.sun.tools.xjc.model.CTypeRef;
053: import com.sun.tools.xjc.model.CValuePropertyInfo;
054: import com.sun.tools.xjc.model.TypeUse;
055: import com.sun.tools.xjc.reader.dtd.bindinfo.BIConversion;
056: import com.sun.tools.xjc.reader.dtd.bindinfo.BIElement;
057: import com.sun.xml.bind.v2.model.core.ID;
058: import com.sun.xml.bind.v2.model.core.WildcardMode;
059: import com.sun.xml.dtdparser.DTDEventListener;
060:
061: import org.xml.sax.Locator;
062:
063: /**
064: * DTD Element.
065: *
066: * <p>
067: * This class extends {@link Term} to participate in the content model tree.
068: *
069: * <p>
070: * This class is repsonsible for binding the element.
071: *
072: * @author Kohsuke Kawaguchi
073: */
074: final class Element extends Term implements Comparable<Element> {
075:
076: /**
077: * Name of the element.
078: */
079: final String name;
080:
081: private final TDTDReader owner;
082:
083: /**
084: * @see DTDEventListener#endContentModel(String, short)
085: */
086: private short contentModelType;
087:
088: private Term contentModel;
089:
090: /**
091: * True if this element is referenced from another element.
092: */
093: boolean isReferenced;
094:
095: /**
096: * If this element maps to a class, that class representation.
097: * Otherwise null.
098: */
099: private CClassInfo classInfo;
100:
101: /**
102: * True if {@link #classInfo} field is computed.
103: */
104: private boolean classInfoComputed;
105:
106: /**
107: * List of attribute properties on this element
108: */
109: final List<CPropertyInfo> attributes = new ArrayList<CPropertyInfo>();
110:
111: /**
112: * Normalized blocks of the content model.
113: */
114: private final List<Block> normalizedBlocks = new ArrayList<Block>();
115:
116: /**
117: * True if this element needs to be a class.
118: *
119: * Currently, if an element is referenced from a construct like (A|B|C),
120: * we require those A,B, and C to be a class.
121: */
122: private boolean mustBeClass;
123:
124: /**
125: * The source location where this element is defined.
126: */
127: private Locator locator;
128:
129: public Element(TDTDReader owner, String name) {
130: this .owner = owner;
131: this .name = name;
132: }
133:
134: void normalize(List<Block> r, boolean optional) {
135: Block o = new Block(optional, false);
136: o.elements.add(this );
137: r.add(o);
138: }
139:
140: void addAllElements(Block b) {
141: b.elements.add(this );
142: }
143:
144: boolean isOptional() {
145: return false;
146: }
147:
148: boolean isRepeated() {
149: return false;
150: }
151:
152: /**
153: * Define its content model.
154: */
155: void define(short contentModelType, Term contentModel,
156: Locator locator) {
157: assert this .contentModel == null; // may not be called twice
158: this .contentModelType = contentModelType;
159: this .contentModel = contentModel;
160: this .locator = locator;
161: contentModel.normalize(normalizedBlocks, false);
162:
163: for (Block b : normalizedBlocks) {
164: if (b.isRepeated || b.elements.size() > 1) {
165: for (Element e : b.elements) {
166: owner.getOrCreateElement(e.name).mustBeClass = true;
167: }
168: }
169: }
170: }
171:
172: /**
173: * When this element is an PCDATA-only content model,
174: * returns the conversion for it. Otherwise the behavior is undefined.
175: */
176: private TypeUse getConversion() {
177: assert contentModel == Term.EMPTY; // this is PCDATA-only element
178:
179: BIElement e = owner.bindInfo.element(name);
180: if (e != null) {
181: BIConversion conv = e.getConversion();
182: if (conv != null)
183: return conv.getTransducer();
184: }
185: return CBuiltinLeafInfo.STRING;
186: }
187:
188: /**
189: * Return null if this class is not bound to a class.
190: */
191: CClassInfo getClassInfo() {
192: if (!classInfoComputed) {
193: classInfoComputed = true;
194: classInfo = calcClass();
195: }
196: return classInfo;
197: }
198:
199: private CClassInfo calcClass() {
200: BIElement e = owner.bindInfo.element(name);
201: if (e == null) {
202: if (contentModelType != DTDEventListener.CONTENT_MODEL_MIXED
203: || !attributes.isEmpty() || mustBeClass)
204: return createDefaultClass();
205: if (contentModel != Term.EMPTY) {
206: throw new UnsupportedOperationException(
207: "mixed content model not supported");
208: } else {
209: // just #PCDATA
210: if (isReferenced)
211: return null;
212: else
213: // if no one else is referencing, assumed to be the root.
214: return createDefaultClass();
215: }
216: } else {
217: return e.clazz;
218: }
219: }
220:
221: private CClassInfo createDefaultClass() {
222: String className = owner.model.getNameConverter().toClassName(
223: name);
224: QName tagName = new QName("", name);
225:
226: return new CClassInfo(owner.model, owner.getTargetPackage(),
227: className, locator, null, tagName, null, null/*TODO*/);
228: }
229:
230: void bind() {
231: CClassInfo ci = getClassInfo();
232: assert ci != null || attributes.isEmpty();
233: for (CPropertyInfo p : attributes)
234: ci.addProperty(p);
235:
236: switch (contentModelType) {
237: case DTDEventListener.CONTENT_MODEL_ANY:
238: CReferencePropertyInfo rp = new CReferencePropertyInfo(
239: "Content", true, true, null, null/*TODO*/, locator);
240: rp.setWildcard(WildcardMode.SKIP);
241: ci.addProperty(rp);
242: return;
243: case DTDEventListener.CONTENT_MODEL_CHILDREN:
244: break; // handling follows
245: case DTDEventListener.CONTENT_MODEL_MIXED:
246: if (contentModel != Term.EMPTY)
247: throw new UnsupportedOperationException(
248: "mixed content model unsupported yet");
249:
250: if (ci != null) {
251: // if this element is mapped to a class, just put one property
252: CValuePropertyInfo p = new CValuePropertyInfo("value",
253: null, null/*TODO*/, locator, getConversion(),
254: null);
255: ci.addProperty(p);
256: }
257: return;
258: case DTDEventListener.CONTENT_MODEL_EMPTY:
259: // no content model
260: assert ci != null;
261: return;
262: }
263:
264: // normalize
265: List<Block> n = new ArrayList<Block>();
266: contentModel.normalize(n, false);
267:
268: {// check collision among Blocks
269: Set<String> names = new HashSet<String>();
270: boolean collision = false;
271:
272: OUTER: for (Block b : n)
273: for (Element e : b.elements)
274: if (!names.add(e.name)) {
275: collision = true;
276: break OUTER;
277: }
278:
279: if (collision) {
280: // collapse all blocks into one
281: Block all = new Block(true, true);
282: for (Block b : n)
283: all.elements.addAll(b.elements);
284: n.clear();
285: n.add(all);
286: }
287: }
288:
289: for (Block b : n) {
290: CElementPropertyInfo p;
291: if (b.isRepeated || b.elements.size() > 1) {
292: // collection
293: StringBuilder name = new StringBuilder();
294: for (Element e : b.elements) {
295: if (name.length() > 0)
296: name.append("Or");
297: name.append(owner.model.getNameConverter()
298: .toPropertyName(e.name));
299: }
300: p = new CElementPropertyInfo(name.toString(),
301: REPEATED_ELEMENT, ID.NONE, null, null,
302: null/*TODO*/, locator, !b.isOptional);
303: for (Element e : b.elements) {
304: CClassInfo child = owner.getOrCreateElement(e.name)
305: .getClassInfo();
306: assert child != null; // we are requiring them to be classes.
307: p.getTypes().add(
308: new CTypeRef(child, new QName("", e.name),
309: null, false, null));
310: }
311: } else {
312: // single property
313: String name = b.elements.iterator().next().name;
314: String propName = owner.model.getNameConverter()
315: .toPropertyName(name);
316:
317: TypeUse refType;
318: Element ref = owner.getOrCreateElement(name);
319: if (ref.getClassInfo() != null)
320: refType = ref.getClassInfo();
321: else {
322: refType = ref.getConversion().getInfo();
323: }
324:
325: p = new CElementPropertyInfo(propName,
326: refType.isCollection() ? REPEATED_VALUE
327: : NOT_REPEATED, ID.NONE, null, null,
328: null/*TODO*/, locator, !b.isOptional);
329:
330: p.getTypes().add(
331: new CTypeRef(refType.getInfo(), new QName("",
332: name), null, false, null));
333: }
334: ci.addProperty(p);
335: }
336: }
337:
338: public int compareTo(Element that) {
339: return this.name.compareTo(that.name);
340: }
341: }
|