| /* |
| * Copyright 2006 Sun Microsystems, Inc. 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. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| */ |
| |
| package com.sun.tools.internal.xjc.reader.relaxng; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.xml.namespace.QName; |
| |
| import com.sun.codemodel.internal.JCodeModel; |
| import com.sun.codemodel.internal.JPackage; |
| import com.sun.tools.internal.xjc.Options; |
| import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo; |
| import com.sun.tools.internal.xjc.model.CClassInfo; |
| import com.sun.tools.internal.xjc.model.CClassInfoParent; |
| import com.sun.tools.internal.xjc.model.CEnumConstant; |
| import com.sun.tools.internal.xjc.model.CEnumLeafInfo; |
| import com.sun.tools.internal.xjc.model.CNonElement; |
| import com.sun.tools.internal.xjc.model.CTypeInfo; |
| import com.sun.tools.internal.xjc.model.Model; |
| import com.sun.tools.internal.xjc.model.TypeUse; |
| import com.sun.xml.internal.bind.api.impl.NameConverter; |
| |
| import com.sun.xml.internal.rngom.digested.DChoicePattern; |
| import com.sun.xml.internal.rngom.digested.DDefine; |
| import com.sun.xml.internal.rngom.digested.DElementPattern; |
| import com.sun.xml.internal.rngom.digested.DPattern; |
| import com.sun.xml.internal.rngom.digested.DPatternWalker; |
| import com.sun.xml.internal.rngom.digested.DRefPattern; |
| import com.sun.xml.internal.rngom.digested.DValuePattern; |
| import com.sun.xml.internal.rngom.nc.NameClass; |
| import com.sun.xml.internal.rngom.xml.util.WellKnownNamespaces; |
| |
| /** |
| * @author Kohsuke Kawaguchi |
| */ |
| public final class RELAXNGCompiler { |
| /** |
| * Schema to compile. |
| */ |
| final DPattern grammar; |
| |
| /** |
| * All named patterns in this schema. |
| */ |
| final Set<DDefine> defs; |
| |
| final Options opts; |
| |
| final Model model; |
| |
| /** |
| * The package to which we generate the code into. |
| */ |
| final JPackage pkg; |
| |
| final Map<String,DatatypeLib> datatypes = new HashMap<String, DatatypeLib>(); |
| |
| /** |
| * Patterns that are mapped to Java concepts. |
| * |
| * <p> |
| * The value is an array because we map elements with finite names |
| * to multiple classes. |
| * |
| * TODO: depending on the type of the key, the type of the values can be further |
| * restricted. Make this into its own class to represent those constraints better. |
| */ |
| final Map<DPattern,CTypeInfo[]> classes = new HashMap<DPattern,CTypeInfo[]>(); |
| |
| /** |
| * Classes that need to be bound. |
| * |
| * The value is the content model to be bound. |
| */ |
| final Map<CClassInfo,DPattern> bindQueue = new HashMap<CClassInfo,DPattern>(); |
| |
| final TypeUseBinder typeUseBinder = new TypeUseBinder(this); |
| |
| public static Model build(DPattern grammar, JCodeModel codeModel, Options opts ) { |
| RELAXNGCompiler compiler = new RELAXNGCompiler(grammar, codeModel, opts); |
| compiler.compile(); |
| return compiler.model; |
| } |
| |
| public RELAXNGCompiler(DPattern grammar, JCodeModel codeModel, Options opts) { |
| this.grammar = grammar; |
| this.opts = opts; |
| this.model = new Model(opts,codeModel, NameConverter.smart,opts.classNameAllocator); |
| |
| datatypes.put("",DatatypeLib.BUILTIN); |
| datatypes.put(WellKnownNamespaces.XML_SCHEMA_DATATYPES,DatatypeLib.XMLSCHEMA); |
| |
| // find all defines |
| DefineFinder deff = new DefineFinder(); |
| grammar.accept(deff); |
| this.defs = deff.defs; |
| |
| if(opts.defaultPackage2!=null) |
| pkg = codeModel._package(opts.defaultPackage2); |
| else |
| if(opts.defaultPackage!=null) |
| pkg = codeModel._package(opts.defaultPackage); |
| else |
| pkg = codeModel.rootPackage(); |
| } |
| |
| private void compile() { |
| // decide which patterns to map to classes |
| promoteElementDefsToClasses(); |
| promoteTypeSafeEnums(); |
| // TODO: promote patterns with <jaxb:class> to classes |
| // TODO: promote 'type' patterns to classes |
| promoteTypePatternsToClasses(); |
| |
| for (Map.Entry<CClassInfo,DPattern> e : bindQueue.entrySet()) |
| bindContentModel(e.getKey(),e.getValue()); |
| } |
| |
| private void bindContentModel(CClassInfo clazz, DPattern pattern) { |
| // first we decide which patterns in it map to properties |
| // then we process each of them by using RawTypeSetBuilder. |
| // much like DefaultParticleBinder in XSD |
| pattern.accept(new ContentModelBinder(this,clazz)); |
| } |
| |
| private void promoteTypeSafeEnums() { |
| // we'll be trying a lot of choices, |
| // and most of them will not be type-safe enum. |
| // using the same list improves the memory efficiency. |
| List<CEnumConstant> members = new ArrayList<CEnumConstant>(); |
| |
| OUTER: |
| for( DDefine def : defs ) { |
| DPattern p = def.getPattern(); |
| if (p instanceof DChoicePattern) { |
| DChoicePattern cp = (DChoicePattern) p; |
| |
| members.clear(); |
| |
| // check if the choice consists of all value patterns |
| // and that they are of the same datatype |
| DValuePattern vp = null; |
| |
| for( DPattern child : cp ) { |
| if(child instanceof DValuePattern) { |
| DValuePattern c = (DValuePattern) child; |
| if(vp==null) |
| vp=c; |
| else { |
| if(!vp.getDatatypeLibrary().equals(c.getDatatypeLibrary()) |
| || !vp.getType().equals(c.getType()) ) |
| continue OUTER; // different type name |
| } |
| |
| members.add(new CEnumConstant( |
| model.getNameConverter().toConstantName(c.getValue()), |
| null, c.getValue(), c.getLocation() |
| )); |
| } else |
| continue OUTER; // not a value |
| } |
| |
| if(members.isEmpty()) |
| continue; // empty choice |
| |
| CNonElement base = CBuiltinLeafInfo.STRING; |
| |
| DatatypeLib lib = datatypes.get(vp.getNs()); |
| if(lib!=null) { |
| TypeUse use = lib.get(vp.getType()); |
| if(use instanceof CNonElement) |
| base = (CNonElement)use; |
| } |
| |
| CEnumLeafInfo xducer = new CEnumLeafInfo(model, null, |
| new CClassInfoParent.Package(pkg), def.getName(), base, |
| new ArrayList<CEnumConstant>(members), |
| null, null/*TODO*/, cp.getLocation()); |
| |
| classes.put(cp,new CTypeInfo[]{xducer}); |
| } |
| } |
| } |
| |
| |
| private void promoteElementDefsToClasses() { |
| // look for elements among named patterns |
| for( DDefine def : defs ) { |
| DPattern p = def.getPattern(); |
| if (p instanceof DElementPattern) { |
| DElementPattern ep = (DElementPattern) p; |
| |
| mapToClass(ep); |
| } |
| } |
| |
| // also look for root elements |
| grammar.accept(new DPatternWalker() { |
| public Void onRef(DRefPattern p) { |
| return null; // stop recursion |
| } |
| |
| public Void onElement(DElementPattern p) { |
| mapToClass(p); |
| return null; |
| } |
| }); |
| } |
| |
| private void mapToClass(DElementPattern p) { |
| NameClass nc = p.getName(); |
| if(nc.isOpen()) |
| return; // infinite name. can't map to a class. |
| |
| Set<QName> names = nc.listNames(); |
| |
| CClassInfo[] types = new CClassInfo[names.size()]; |
| int i=0; |
| for( QName n : names ) { |
| // TODO: read class names from customization |
| String name = model.getNameConverter().toClassName(n.getLocalPart()); |
| |
| bindQueue.put( |
| types[i++] = new CClassInfo(model,pkg,name,p.getLocation(),null,n,null,null/*TODO*/), |
| p.getChild() ); |
| } |
| |
| classes.put(p,types); |
| } |
| |
| /** |
| * Looks for named patterns that are not bound to classes so far, |
| * but that can be bound to classes. |
| */ |
| private void promoteTypePatternsToClasses() { |
| |
| // for( DDefine def : defs ) { |
| // ; |
| // |
| // def.getPattern().accept(new InheritanceChecker()); |
| // } |
| } |
| } |