blob: c532e37309631fad8c755b8adfe6b5845e703499 [file] [log] [blame]
/*
* 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());
// }
}
}