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.relaxng;
038:
039: import java.util.ArrayList;
040: import java.util.HashMap;
041: import java.util.List;
042: import java.util.Map;
043: import java.util.Set;
044:
045: import javax.xml.namespace.QName;
046:
047: import com.sun.codemodel.JCodeModel;
048: import com.sun.codemodel.JPackage;
049: import com.sun.tools.xjc.Options;
050: import com.sun.tools.xjc.model.CBuiltinLeafInfo;
051: import com.sun.tools.xjc.model.CClassInfo;
052: import com.sun.tools.xjc.model.CClassInfoParent;
053: import com.sun.tools.xjc.model.CEnumConstant;
054: import com.sun.tools.xjc.model.CEnumLeafInfo;
055: import com.sun.tools.xjc.model.CNonElement;
056: import com.sun.tools.xjc.model.CTypeInfo;
057: import com.sun.tools.xjc.model.Model;
058: import com.sun.tools.xjc.model.TypeUse;
059: import com.sun.xml.bind.api.impl.NameConverter;
060:
061: import org.kohsuke.rngom.digested.DChoicePattern;
062: import org.kohsuke.rngom.digested.DDefine;
063: import org.kohsuke.rngom.digested.DElementPattern;
064: import org.kohsuke.rngom.digested.DPattern;
065: import org.kohsuke.rngom.digested.DPatternWalker;
066: import org.kohsuke.rngom.digested.DRefPattern;
067: import org.kohsuke.rngom.digested.DValuePattern;
068: import org.kohsuke.rngom.nc.NameClass;
069: import org.kohsuke.rngom.xml.util.WellKnownNamespaces;
070:
071: /**
072: * @author Kohsuke Kawaguchi
073: */
074: public final class RELAXNGCompiler {
075: /**
076: * Schema to compile.
077: */
078: final DPattern grammar;
079:
080: /**
081: * All named patterns in this schema.
082: */
083: final Set<DDefine> defs;
084:
085: final Options opts;
086:
087: final Model model;
088:
089: /**
090: * The package to which we generate the code into.
091: */
092: final JPackage pkg;
093:
094: final Map<String, DatatypeLib> datatypes = new HashMap<String, DatatypeLib>();
095:
096: /**
097: * Patterns that are mapped to Java concepts.
098: *
099: * <p>
100: * The value is an array because we map elements with finite names
101: * to multiple classes.
102: *
103: * TODO: depending on the type of the key, the type of the values can be further
104: * restricted. Make this into its own class to represent those constraints better.
105: */
106: final Map<DPattern, CTypeInfo[]> classes = new HashMap<DPattern, CTypeInfo[]>();
107:
108: /**
109: * Classes that need to be bound.
110: *
111: * The value is the content model to be bound.
112: */
113: final Map<CClassInfo, DPattern> bindQueue = new HashMap<CClassInfo, DPattern>();
114:
115: final TypeUseBinder typeUseBinder = new TypeUseBinder(this );
116:
117: public static Model build(DPattern grammar, JCodeModel codeModel,
118: Options opts) {
119: RELAXNGCompiler compiler = new RELAXNGCompiler(grammar,
120: codeModel, opts);
121: compiler.compile();
122: return compiler.model;
123: }
124:
125: public RELAXNGCompiler(DPattern grammar, JCodeModel codeModel,
126: Options opts) {
127: this .grammar = grammar;
128: this .opts = opts;
129: this .model = new Model(opts, codeModel, NameConverter.smart,
130: opts.classNameAllocator, null);
131:
132: datatypes.put("", DatatypeLib.BUILTIN);
133: datatypes.put(WellKnownNamespaces.XML_SCHEMA_DATATYPES,
134: DatatypeLib.XMLSCHEMA);
135:
136: // find all defines
137: DefineFinder deff = new DefineFinder();
138: grammar.accept(deff);
139: this .defs = deff.defs;
140:
141: if (opts.defaultPackage2 != null)
142: pkg = codeModel._package(opts.defaultPackage2);
143: else if (opts.defaultPackage != null)
144: pkg = codeModel._package(opts.defaultPackage);
145: else
146: pkg = codeModel.rootPackage();
147: }
148:
149: private void compile() {
150: // decide which patterns to map to classes
151: promoteElementDefsToClasses();
152: promoteTypeSafeEnums();
153: // TODO: promote patterns with <jaxb:class> to classes
154: // TODO: promote 'type' patterns to classes
155: promoteTypePatternsToClasses();
156:
157: for (Map.Entry<CClassInfo, DPattern> e : bindQueue.entrySet())
158: bindContentModel(e.getKey(), e.getValue());
159: }
160:
161: private void bindContentModel(CClassInfo clazz, DPattern pattern) {
162: // first we decide which patterns in it map to properties
163: // then we process each of them by using RawTypeSetBuilder.
164: // much like DefaultParticleBinder in XSD
165: pattern.accept(new ContentModelBinder(this , clazz));
166: }
167:
168: private void promoteTypeSafeEnums() {
169: // we'll be trying a lot of choices,
170: // and most of them will not be type-safe enum.
171: // using the same list improves the memory efficiency.
172: List<CEnumConstant> members = new ArrayList<CEnumConstant>();
173:
174: OUTER: for (DDefine def : defs) {
175: DPattern p = def.getPattern();
176: if (p instanceof DChoicePattern) {
177: DChoicePattern cp = (DChoicePattern) p;
178:
179: members.clear();
180:
181: // check if the choice consists of all value patterns
182: // and that they are of the same datatype
183: DValuePattern vp = null;
184:
185: for (DPattern child : cp) {
186: if (child instanceof DValuePattern) {
187: DValuePattern c = (DValuePattern) child;
188: if (vp == null)
189: vp = c;
190: else {
191: if (!vp.getDatatypeLibrary().equals(
192: c.getDatatypeLibrary())
193: || !vp.getType()
194: .equals(c.getType()))
195: continue OUTER; // different type name
196: }
197:
198: members.add(new CEnumConstant(model
199: .getNameConverter().toConstantName(
200: c.getValue()), null, c
201: .getValue(), c.getLocation()));
202: } else
203: continue OUTER; // not a value
204: }
205:
206: if (members.isEmpty())
207: continue; // empty choice
208:
209: CNonElement base = CBuiltinLeafInfo.STRING;
210:
211: DatatypeLib lib = datatypes.get(vp.getNs());
212: if (lib != null) {
213: TypeUse use = lib.get(vp.getType());
214: if (use instanceof CNonElement)
215: base = (CNonElement) use;
216: }
217:
218: CEnumLeafInfo xducer = new CEnumLeafInfo(model, null,
219: new CClassInfoParent.Package(pkg), def
220: .getName(), base,
221: new ArrayList<CEnumConstant>(members), null,
222: null/*TODO*/, cp.getLocation());
223:
224: classes.put(cp, new CTypeInfo[] { xducer });
225: }
226: }
227: }
228:
229: private void promoteElementDefsToClasses() {
230: // look for elements among named patterns
231: for (DDefine def : defs) {
232: DPattern p = def.getPattern();
233: if (p instanceof DElementPattern) {
234: DElementPattern ep = (DElementPattern) p;
235:
236: mapToClass(ep);
237: }
238: }
239:
240: // also look for root elements
241: grammar.accept(new DPatternWalker() {
242: public Void onRef(DRefPattern p) {
243: return null; // stop recursion
244: }
245:
246: public Void onElement(DElementPattern p) {
247: mapToClass(p);
248: return null;
249: }
250: });
251: }
252:
253: private void mapToClass(DElementPattern p) {
254: NameClass nc = p.getName();
255: if (nc.isOpen())
256: return; // infinite name. can't map to a class.
257:
258: Set<QName> names = nc.listNames();
259:
260: CClassInfo[] types = new CClassInfo[names.size()];
261: int i = 0;
262: for (QName n : names) {
263: // TODO: read class names from customization
264: String name = model.getNameConverter().toClassName(
265: n.getLocalPart());
266:
267: bindQueue.put(types[i++] = new CClassInfo(model, pkg, name,
268: p.getLocation(), null, n, null, null/*TODO*/), p
269: .getChild());
270: }
271:
272: classes.put(p, types);
273: }
274:
275: /**
276: * Looks for named patterns that are not bound to classes so far,
277: * but that can be bound to classes.
278: */
279: private void promoteTypePatternsToClasses() {
280:
281: // for( DDefine def : defs ) {
282: // ;
283: //
284: // def.getPattern().accept(new InheritanceChecker());
285: // }
286: }
287: }
|