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.xmlschema;
038:
039: import java.util.ArrayList;
040: import java.util.Collection;
041: import java.util.Collections;
042: import java.util.HashMap;
043: import java.util.Hashtable;
044: import java.util.List;
045: import java.util.Map;
046:
047: import com.sun.tools.xjc.model.CClassInfo;
048: import com.sun.tools.xjc.model.CPropertyInfo;
049: import com.sun.tools.xjc.model.CReferencePropertyInfo;
050: import com.sun.tools.xjc.reader.xmlschema.bindinfo.BIProperty;
051: import com.sun.xml.xsom.XSElementDecl;
052: import com.sun.xml.xsom.XSModelGroup;
053: import com.sun.xml.xsom.XSModelGroupDecl;
054: import com.sun.xml.xsom.XSParticle;
055: import com.sun.xml.xsom.XSTerm;
056: import com.sun.xml.xsom.XSWildcard;
057: import com.sun.xml.xsom.visitor.XSTermVisitor;
058:
059: /**
060: * {@link ParticleBinder} that follows the JAXB spec.
061: *
062: * @author Kohsuke Kawaguchi
063: */
064: final class DefaultParticleBinder extends ParticleBinder {
065:
066: @Override
067: public void build(XSParticle p, Collection<XSParticle> forcedProps) {
068: Checker checker = checkCollision(p, forcedProps);
069:
070: if (checker.hasNameCollision()) {
071: CReferencePropertyInfo prop = new CReferencePropertyInfo(
072: getCurrentBean().getBaseClass() == null ? "Content"
073: : "Rest", true, false, p, builder
074: .getBindInfo(p).toCustomizationList(), p
075: .getLocator());
076: RawTypeSetBuilder.build(p, false).addTo(prop);
077: prop.javadoc = Messages.format(
078: Messages.MSG_FALLBACK_JAVADOC, checker
079: .getCollisionInfo().toString());
080:
081: getCurrentBean().addProperty(prop);
082: } else {
083: new Builder(checker.markedParticles).particle(p);
084: }
085: }
086:
087: @Override
088: public boolean checkFallback(XSParticle p) {
089: return checkCollision(p, Collections.<XSParticle> emptyList())
090: .hasNameCollision();
091: }
092:
093: private Checker checkCollision(XSParticle p,
094: Collection<XSParticle> forcedProps) {
095: // scan the tree by a checker.
096: Checker checker = new Checker(forcedProps);
097:
098: CClassInfo super Class = getCurrentBean().getBaseClass();
099:
100: if (super Class != null)
101: checker.readSuperClass(super Class);
102: checker.particle(p);
103:
104: return checker;
105: }
106:
107: /**
108: * Marks particles that need to be mapped to properties,
109: * by reading customization info.
110: */
111: private final class Checker implements XSTermVisitor {
112:
113: Checker(Collection<XSParticle> forcedProps) {
114: this .forcedProps = forcedProps;
115: }
116:
117: boolean hasNameCollision() {
118: return collisionInfo != null;
119: }
120:
121: CollisionInfo getCollisionInfo() {
122: return collisionInfo;
123: }
124:
125: /**
126: * If a collision is found, this field will be non-null.
127: */
128: private CollisionInfo collisionInfo = null;
129:
130: /** Used to check name collision. */
131: private final NameCollisionChecker cchecker = new NameCollisionChecker();
132:
133: /**
134: * @see DefaultParticleBinder#build(XSParticle, Collection<com.sun.xml.xsom.XSParticle>)
135: */
136: private final Collection<XSParticle> forcedProps;
137:
138: public void particle(XSParticle p) {
139:
140: if (getLocalPropCustomization(p) != null
141: || builder.getLocalDomCustomization(p) != null) {
142: // if a property customization is specfied,
143: // check that value and turn around.
144: check(p);
145: mark(p);
146: return;
147: }
148:
149: XSTerm t = p.getTerm();
150:
151: if (p.isRepeated()
152: && (t.isModelGroup() || t.isModelGroupDecl())) {
153: // a repeated model group gets its own property
154: mark(p);
155: return;
156: }
157:
158: if (forcedProps.contains(p)) {
159: // this particle must become a property
160: mark(p);
161: return;
162: }
163:
164: outerParticle = p;
165: t.visit(this );
166: }
167:
168: /**
169: * This field points to the parent XSParticle.
170: * The value is only valid when we are processing XSTerm.
171: */
172: private XSParticle outerParticle;
173:
174: public void elementDecl(XSElementDecl decl) {
175: check(outerParticle);
176: mark(outerParticle);
177: }
178:
179: public void modelGroup(XSModelGroup mg) {
180: // choice gets mapped to a property
181: if (mg.getCompositor() == XSModelGroup.Compositor.CHOICE
182: && builder.getGlobalBinding()
183: .isChoiceContentPropertyEnabled()) {
184: mark(outerParticle);
185: return;
186: }
187:
188: for (XSParticle child : mg.getChildren())
189: particle(child);
190: }
191:
192: public void modelGroupDecl(XSModelGroupDecl decl) {
193: modelGroup(decl.getModelGroup());
194: }
195:
196: public void wildcard(XSWildcard wc) {
197: mark(outerParticle);
198: }
199:
200: void readSuperClass(CClassInfo ci) {
201: cchecker.readSuperClass(ci);
202: }
203:
204: /**
205: * Checks the name collision of a newly found particle.
206: */
207: private void check(XSParticle p) {
208: if (collisionInfo == null)
209: collisionInfo = cchecker.check(p);
210: }
211:
212: /**
213: * Marks a particle that it's going to be mapped to a property.
214: */
215: private void mark(XSParticle p) {
216: markedParticles.put(p, computeLabel(p));
217: }
218:
219: /**
220: * Marked particles.
221: *
222: * A map from XSParticle to its label.
223: */
224: public final Map<XSParticle, String> markedParticles = new HashMap<XSParticle, String>();
225:
226: /**
227: * Checks name collisions among particles that belong to sequences.
228: */
229: private final class NameCollisionChecker {
230:
231: /**
232: * Checks the label conflict of a particle.
233: * This method shall be called for each marked particle.
234: *
235: * @return
236: * a description of a collision if a name collision is
237: * found. Otherwise null.
238: */
239: CollisionInfo check(XSParticle p) {
240: // this can be used for particles with a property customization,
241: // which may not have element declaration as its term.
242: // // we only check particles with element declarations.
243: // _assert( p.getTerm().isElementDecl() );
244:
245: String label = computeLabel(p);
246: if (occupiedLabels.containsKey(label)) {
247: // collide with occupied labels
248: return new CollisionInfo(label, p.getLocator(),
249: occupiedLabels.get(label).locator);
250: }
251:
252: for (XSParticle jp : particles) {
253: if (!check(p, jp)) {
254: // problem was found. no need to check further
255: return new CollisionInfo(label, p.getLocator(),
256: jp.getLocator());
257: }
258: }
259: particles.add(p);
260: return null;
261: }
262:
263: /** List of particles reported through the check method. */
264: private final List<XSParticle> particles = new ArrayList<XSParticle>();
265:
266: /**
267: * Label names already used in the base type.
268: */
269: private final Map<String, CPropertyInfo> occupiedLabels = new HashMap<String, CPropertyInfo>();
270:
271: /**
272: * Checks the conflict of two particles.
273: * @return
274: * true if the check was successful.
275: */
276: private boolean check(XSParticle p1, XSParticle p2) {
277: return !computeLabel(p1).equals(computeLabel(p2));
278: }
279:
280: /**
281: * Reads fields of the super class and includes them
282: * to name collision tests.
283: */
284: void readSuperClass(CClassInfo base) {
285: for (; base != null; base = base.getBaseClass()) {
286: for (CPropertyInfo p : base.getProperties())
287: occupiedLabels.put(p.getName(true), p);
288: }
289: }
290: }
291:
292: /** Keep the computed label names for particles. */
293: private final Map<XSParticle, String> labelCache = new Hashtable<XSParticle, String>();
294:
295: /**
296: * Hides the computeLabel method of the outer class
297: * and adds caching.
298: */
299: private String computeLabel(XSParticle p) {
300: String label = labelCache.get(p);
301: if (label == null)
302: labelCache.put(p, label = DefaultParticleBinder.this
303: .computeLabel(p));
304: return label;
305: }
306: }
307:
308: /**
309: * Builds properties by using the result computed by Checker
310: */
311: private final class Builder implements XSTermVisitor {
312: Builder(Map<XSParticle, String> markedParticles) {
313: this .markedParticles = markedParticles;
314: }
315:
316: /** All marked particles. */
317: private final Map<XSParticle, String/*label*/> markedParticles;
318:
319: /**
320: * When we are visiting inside an optional particle, this flag
321: * is set to true.
322: *
323: * <p>
324: * This allows us to correctly generate primitive/boxed types.
325: */
326: private boolean insideOptionalParticle;
327:
328: /** Returns true if a particle is marked. */
329: private boolean marked(XSParticle p) {
330: return markedParticles.containsKey(p);
331: }
332:
333: /** Gets a label of a particle. */
334: private String getLabel(XSParticle p) {
335: return markedParticles.get(p);
336: }
337:
338: public void particle(XSParticle p) {
339: XSTerm t = p.getTerm();
340:
341: if (marked(p)) {
342: BIProperty cust = BIProperty.getCustomization(p);
343: CPropertyInfo prop = cust
344: .createElementOrReferenceProperty(getLabel(p),
345: false, p, RawTypeSetBuilder.build(p,
346: insideOptionalParticle));
347: getCurrentBean().addProperty(prop);
348: } else {
349: // repeated model groups should have been marked already
350: assert !p.isRepeated();
351:
352: boolean oldIOP = insideOptionalParticle;
353: insideOptionalParticle |= p.getMinOccurs() == 0;
354: // this is an unmarked particle
355: t.visit(this );
356: insideOptionalParticle = oldIOP;
357: }
358: }
359:
360: public void elementDecl(XSElementDecl e) {
361: // because the corresponding particle must be marked.
362: assert false;
363: }
364:
365: public void wildcard(XSWildcard wc) {
366: // because the corresponding particle must be marked.
367: assert false;
368: }
369:
370: public void modelGroupDecl(XSModelGroupDecl decl) {
371: modelGroup(decl.getModelGroup());
372: }
373:
374: public void modelGroup(XSModelGroup mg) {
375: boolean oldIOP = insideOptionalParticle;
376: insideOptionalParticle |= mg.getCompositor() == XSModelGroup.CHOICE;
377:
378: for (XSParticle p : mg.getChildren())
379: particle(p);
380:
381: insideOptionalParticle = oldIOP;
382: }
383: }
384: }
|