| /* Copyright 2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.xmlbeans.impl.binding.compile; |
| |
| import org.apache.xmlbeans.impl.binding.bts.*; |
| import org.apache.xmlbeans.impl.binding.tylar.TylarWriter; |
| import org.apache.xmlbeans.impl.binding.tylar.TylarConstants; |
| import org.apache.xmlbeans.impl.binding.tylar.ExplodedTylarImpl; |
| import org.apache.xmlbeans.impl.jam.*; |
| import org.apache.xmlbeans.impl.common.XMLChar; |
| import org.apache.xmlbeans.SchemaTypeSystem; |
| import org.apache.xmlbeans.XmlException; |
| import org.w3.x2001.xmlSchema.*; |
| import javax.xml.namespace.QName; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.HashMap; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.io.IOException; |
| import java.io.StringWriter; |
| |
| |
| /** |
| * Takes a set of Java source inputs and generates a set of XML schemas to |
| * which those input should be bound, as well as a binding configuration file |
| * which describes to the runtime subsystem how the un/marshalling should |
| * be performed. |
| * |
| * @author Patrick Calahan <pcal@bea.com> |
| */ |
| public class Java2Schema extends BindingCompiler { |
| |
| // ========================================================================= |
| // Constants |
| |
| private static final String JAVA_URI_SCHEME = "java:"; |
| private static final String JAVA_NAMESPACE_URI = "language_builtins"; |
| private static final String JAVA_PACKAGE_PREFIX = "java."; |
| |
| public static final String TAG_CT = "xsdgen:complexType"; |
| public static final String TAG_CT_EXCLUDE = TAG_CT+".exclude"; |
| public static final String TAG_CT_TYPENAME = TAG_CT+".typeName"; |
| public static final String TAG_CT_TARGETNS = TAG_CT+".targetNamespace"; |
| public static final String TAG_CT_ROOT = TAG_CT+".rootElement"; |
| public static final String TAG_CT_IGNORESUPER = TAG_CT+".ignoreSuper"; |
| |
| private static final String TAG_EL = "xsdgen:element"; |
| |
| public static final String TAG_EL_NAME = TAG_EL+".name"; |
| public static final String TAG_EL_NILLABLE = TAG_EL+".nillable"; |
| public static final String TAG_EL_EXCLUDE = TAG_EL+".exclude"; |
| public static final String TAG_EL_ASTYPE = TAG_EL+".astype"; |
| |
| public static final String TAG_AT = "xsdgen:attribute"; |
| public static final String TAG_AT_NAME = TAG_AT+".name"; |
| |
| public static final String TAG_ISSETTER = "xsdgen:isSetMethodFor"; |
| |
| // this is the character that replaces invalid characters when creating new |
| // xml names. |
| private static final char SAFE_CHAR = '_'; |
| |
| // ========================================================================= |
| // Variables |
| |
| // the bindings that we're building up |
| private BindingFile mBindingFile; |
| |
| // the full loader: bindingFile + baseLoader |
| private BindingLoader mLoader; |
| |
| // maps a targetnamespace (string) to a schemadocument that we are creating |
| private Map mTns2Schemadoc = new HashMap(); |
| |
| // the input classes |
| private JClass[] mClasses; |
| |
| // ========================================================================= |
| // Constructors |
| |
| public Java2Schema(JClass[] classesToBind) { |
| if (classesToBind == null) { |
| throw new IllegalArgumentException("null classes"); |
| } |
| mClasses = classesToBind; |
| } |
| |
| // ======================================================================== |
| // BindingCompiler implementation |
| |
| /** |
| * Does the binding work on the inputs passed to the constructor and writes |
| * out the tylar. |
| */ |
| protected void internalBind(TylarWriter writer) { |
| mBindingFile = new BindingFile(); |
| mLoader = CompositeBindingLoader.forPath |
| (new BindingLoader[] {mBindingFile, super.getBaseBindingLoader()}); |
| //This does the binding |
| for(int i=0; i<mClasses.length; i++) { |
| if (!getAnnotation(mClasses[i],TAG_CT_EXCLUDE,false)) { |
| getBindingTypeFor(mClasses[i]); |
| } |
| } |
| // |
| SchemaDocument[] xsds = null; |
| try { |
| writer.writeBindingFile(mBindingFile); |
| //REVIEW maybe we should just include the schema and let super class write it out? |
| xsds = getGeneratedSchemaDocuments(); |
| for(int i=0; i<xsds.length; i++) { |
| writer.writeSchema(xsds[i],"schema-"+i+".xsd"); |
| } |
| } catch(IOException ioe) { |
| logError(ioe); |
| } |
| SchemaTypeSystem sts = null; |
| { |
| try { |
| sts = Schema2JavaTask.createSchemaTypeSystem(xsds); |
| if (sts == null) { |
| throw new IllegalStateException("createSchemaTypeSystem returned null"); |
| } |
| } catch(XmlException xe) { |
| ExplodedTylarImpl.showXsbError(xe,null,"write",TylarConstants.SHOW_XSB_ERRORS); |
| } |
| if (sts != null) { |
| try { |
| writer.writeSchemaTypeSystem(sts); |
| } catch(IOException ioe) { |
| ExplodedTylarImpl.showXsbError(ioe,null,"compile",TylarConstants.SHOW_XSB_ERRORS); |
| } |
| } |
| } |
| } |
| |
| // ======================================================================== |
| // Private methods |
| |
| private SchemaDocument.Schema findOrCreateSchema(String tns) { |
| if (tns == null) throw new IllegalArgumentException(); |
| tns = tns.trim(); |
| SchemaDocument doc = (SchemaDocument)mTns2Schemadoc.get(tns); |
| if (doc == null) { |
| doc = SchemaDocument.Factory.newInstance(); |
| doc.addNewSchema().setTargetNamespace(tns); |
| mTns2Schemadoc.put(tns,doc); |
| } |
| return doc.getSchema(); |
| } |
| |
| private SchemaDocument[] getGeneratedSchemaDocuments() { |
| Collection list = mTns2Schemadoc.values(); |
| SchemaDocument[] out = new SchemaDocument[list.size()]; |
| list.toArray(out); |
| return out; |
| } |
| |
| |
| /** |
| * Returns a bts BindingType for the given JClass. If such a type |
| * has not yet been registered with the loader, it will be created. |
| * |
| * @param clazz Java type for which to return a binding. |
| */ |
| private BindingType getBindingTypeFor(JClass clazz) { |
| BindingTypeName btn = mLoader.lookupTypeFor(getJavaName(clazz)); |
| if (btn != null) { |
| BindingType out = mLoader.getBindingType(btn); |
| if (out != null) return out; |
| } |
| return createBindingTypeFor(clazz); |
| } |
| |
| /** |
| * Creates a bts BindingType for the given JClass and registers t with the |
| * loader. Note that this method assumes that a BindingType does not |
| * already exist for the given JClass. |
| * |
| * @param clazz Java type for which to generate a binding. |
| */ |
| private BindingType createBindingTypeFor(JClass clazz) { |
| // create the schema type |
| SchemaDocument.Schema schema = findOrCreateSchema(getTargetNamespace(clazz)); |
| TopLevelComplexType xsType = schema.addNewComplexType(); |
| String tns = getTargetNamespace(clazz); |
| String xsdName = getAnnotation(clazz,TAG_CT_TYPENAME,clazz.getSimpleName()); |
| QName qname = new QName(tns,xsdName); |
| xsType.setName(xsdName); |
| // deal with inheritance - see if it extends anything |
| JClass superclass = clazz.getSuperclass(); |
| // we have to remember whether we created an ExtensionType because that |
| // is where the sequence of properties have to go - note that this |
| // gets passed into the SchemaPropertyFacade created below. It's |
| // unfortunate that the SchemaDocument model does not allow us to deal |
| // with this kind of thing in a more elegant and polymorphic way. |
| ExtensionType extType = null; |
| BindingType superBindingType = null; |
| if (superclass != null && !superclass.isObjectType() && |
| !getAnnotation(clazz,TAG_CT_IGNORESUPER,false)) { |
| // FIXME we're ignoring interfaces at the moment |
| superBindingType = getBindingTypeFor(superclass); |
| ComplexContentDocument.ComplexContent ccd = xsType.addNewComplexContent(); |
| extType = ccd.addNewExtension(); |
| extType.setBase(superBindingType.getName().getXmlName().getQName()); |
| } |
| // create a binding type |
| BindingTypeName btname = BindingTypeName.forPair(getJavaName(clazz), |
| XmlTypeName.forTypeNamed(qname)); |
| ByNameBean bindType = new ByNameBean(btname); |
| mBindingFile.addBindingType(bindType,true,true); |
| if (clazz.isPrimitiveType()) { |
| // it's good to have registerd the dummy type, but don't go further |
| logError("Unexpected simple type",clazz); |
| return bindType; |
| } |
| |
| //add super's props first |
| if (superBindingType != null) { |
| //REVIEW: will it ever be possible to have another type as the super type? |
| //REVIEW: is copy by ref safe here? |
| //TODO: deal with java->schema name collisions across inherited types |
| ByNameBean super_type = (ByNameBean)superBindingType; |
| for(Iterator itr = super_type.getProperties().iterator() ; itr.hasNext() ; ) { |
| final QNameProperty prop = (QNameProperty)itr.next(); |
| bindType.addProperty(prop); |
| } |
| bindType.setAnyAttributeProperty(super_type.getAnyAttributeProperty()); |
| bindType.setAnyElementProperty(super_type.getAnyElementProperty()); |
| } |
| |
| String rootName = getAnnotation(clazz,TAG_CT_ROOT,null); |
| if (rootName != null) { |
| QName rootQName = new QName(tns, rootName); |
| BindingTypeName docBtName = |
| BindingTypeName.forPair(getJavaName(clazz), |
| XmlTypeName.forGlobalName(XmlTypeName.ELEMENT, rootQName)); |
| SimpleDocumentBinding sdb = new SimpleDocumentBinding(docBtName); |
| sdb.setTypeOfElement(btname.getXmlName()); |
| mBindingFile.addBindingType(sdb,true,true); |
| } |
| // run through the class' properties to populate the binding and xsdtypes |
| SchemaPropertyFacade facade = new SchemaPropertyFacade(xsType,extType,bindType,tns); |
| Map props2issetters = new HashMap(); |
| getIsSetters(clazz,props2issetters); |
| bindProperties(clazz.getDeclaredProperties(),props2issetters,facade); |
| facade.finish(); |
| // check to see if they want to create a root elements from this type |
| JAnnotation[] anns = getNamedTags(clazz.getAllJavadocTags(),TAG_CT_ROOT); |
| for(int i=0; i<anns.length; i++) { |
| TopLevelElement root = schema.addNewElement(); |
| root.setName(makeNcNameSafe(anns[i].getValue |
| (JAnnotation.SINGLE_VALUE_NAME).asString())); |
| root.setType(qname); |
| // FIXME still not entirely clear to me what we should do about |
| // the binding file here |
| } |
| return bindType; |
| } |
| |
| private void getIsSetters(JClass clazz, Map outPropname2jmethod) { |
| JMethod[] methods = clazz.getDeclaredMethods(); |
| for(int i=0; i<methods.length; i++) { |
| JAnnotation ann = methods[i].getAnnotation(TAG_ISSETTER); |
| if (ann != null) { |
| if (!methods[i].getReturnType().getQualifiedName().equals("boolean")) { |
| logWarning("Method "+methods[i].getQualifiedName()+" is marked "+ |
| TAG_ISSETTER+"\nbut it does not return boolean."+ |
| "Ignoring."); |
| continue; |
| } |
| if (methods[i].getParameters().length > 0) { |
| logWarning("Method "+methods[i].getQualifiedName()+" is marked "+ |
| TAG_ISSETTER+"\nbut takes arguments. Ignoring."); |
| continue; |
| } |
| JAnnotationValue propNameVal = ann.getValue(JAnnotation.SINGLE_VALUE_NAME); |
| if (propNameVal == null) { |
| logWarning("Method "+methods[i].getQualifiedName()+" is marked "+ |
| TAG_ISSETTER+"\nbut but no property name is given. Ignoring"); |
| continue; |
| } |
| outPropname2jmethod.put(propNameVal.asString(),methods[i]); |
| } |
| } |
| } |
| |
| /** |
| * Runs through a set of JProperties to creates schema and bts elements |
| * to represent those properties. Note that the details of manipulating the |
| * schema and bts are encapsulated within the supplied SchemaPropertyFacade; |
| * this method is only responsible for inspecting the properties and their |
| * annotations and setting the correct attributes on the facade. |
| * |
| * @param props Array of JProperty objects to potentially be bound. |
| * @param facade Allows us to create and manipulate properties, |
| * hides the dirty work |
| */ |
| private void bindProperties(JProperty[] props, |
| Map props2issetters, |
| SchemaPropertyFacade facade) { |
| for(int i=0; i<props.length; i++) { |
| if (getAnnotation(props[i],TAG_EL_EXCLUDE,false)) { |
| logVerbose("Marked excluded, skipping",props[i]); |
| continue; |
| } |
| if (props[i].getGetter() == null || props[i].getSetter() == null) { |
| logVerbose("Does not have both getter and setter, skipping",props[i]); |
| continue; // REVIEW this might have to change someday |
| } |
| String propName; |
| { // determine the property name to use and set it |
| propName = getAnnotation(props[i], TAG_AT_NAME, null); |
| if (propName != null) { |
| facade.newAttributeProperty(props[i]); |
| } else { |
| facade.newElementProperty(props[i]); |
| propName = getAnnotation(props[i], TAG_EL_NAME, |
| props[i].getSimpleName()); |
| } |
| assert propName != null; |
| facade.setSchemaName(propName); |
| } |
| { // determine the property type to use and set it |
| JClass propType = null; |
| String annotatedType = getAnnotation(props[i],TAG_EL_ASTYPE,null); |
| if (annotatedType == null) { |
| facade.setType(propType = props[i].getType()); |
| } else { |
| if (props[i].getType().isArrayType()) { |
| //THIS IS A QUICK GROSS HACK THAT SHOULD BE REMOVED. |
| //IF SOMEONE WANTS TO AS TYPE AN ARRAY PROPERTY, THEY NEED |
| //TO ASTYPE IT TO THE ARRAY TYPE THEMSELVES |
| annotatedType = "[L"+annotatedType+";"; |
| } |
| propType = props[i].getType().forName(annotatedType); |
| if (propType.isUnresolvedType()) { |
| logError("Could not find class named '"+ |
| propType.getQualifiedName()+"'",props[i]); |
| } else { |
| facade.setType(propType); |
| } |
| } |
| } |
| { // set the getters and setters |
| facade.setGetter(props[i].getGetter()); |
| facade.setSetter(props[i].getSetter()); |
| } |
| { |
| JMethod issetter = (JMethod)props2issetters.get(propName); |
| if (issetter != null) facade.setIssetter(issetter); |
| } |
| { // determine if the property is nillable |
| JAnnotation a = props[i].getAnnotation(TAG_EL_NILLABLE); |
| if (a != null) { |
| // if the tag is there but empty, set it to true. is that weird? |
| JAnnotationValue val = a.getValue(JAnnotation.SINGLE_VALUE_NAME); |
| if (val == null || val.asString().trim().length() == 0) { |
| facade.setNillable(true); |
| } else { |
| facade.setNillable(val.asBoolean()); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * Returns a JavaTypeName for the given JClass. Might want to pool these. |
| */ |
| private JavaTypeName getJavaName(JClass jc) { |
| return JavaTypeName.forString(jc.getQualifiedName()); |
| } |
| |
| /** |
| * Returns the string value of a named annotation, or the provided default |
| * if the annotation is not present. |
| * REVIEW seems like having this functionality in jam_old would be nice |
| */ |
| private String getAnnotation(JAnnotatedElement elem, |
| String annName, |
| String dflt) { |
| //System.out.print("checking for "+annName+" on "+elem.getQualifiedName()); |
| JAnnotation ann = getAnnotation(elem,annName); |
| if (ann == null) { |
| //System.out.println("...no annotation"); |
| return dflt; |
| } |
| JAnnotationValue val = ann.getValue(JAnnotation.SINGLE_VALUE_NAME); |
| if (val == null) { |
| //System.out.println("...no value!!!"); |
| return dflt; |
| } |
| //System.out.println("\n\n\n...value of "+annName+" is "+val.asString()+"!!!!!!!!!"); |
| return val.asString(); |
| } |
| |
| /** |
| * Returns the boolean value of a named annotation, or the provided default |
| * if the annotation is not present. |
| * REVIEW seems like having this functionality in jam_old would be nice |
| */ |
| private boolean getAnnotation(JAnnotatedElement elem, |
| String annName, |
| boolean dflt) { |
| //System.out.print("checking for "+annName+" on "+elem.getQualifiedName()); |
| JAnnotation ann = getAnnotation(elem,annName); |
| if (ann == null) { |
| //System.out.println("...no annotation"); |
| return dflt; |
| } |
| JAnnotationValue val = ann.getValue(JAnnotation.SINGLE_VALUE_NAME); |
| if (val == null || val.asString().length() == 0) { |
| //System.out.println("\n\n\n...no value, returning true!!!"); |
| //this is a little bit gross. the logic here is that if the tag is |
| //present but empty, it actually is a true value. E.g., an empty |
| //@exclude tag means "yes, do exclude." |
| return true; |
| } |
| //System.out.println("\n\n\n...value of "+annName+" is "+val.asBoolean()+"!!!!!!!!!"); |
| return val.asBoolean(); |
| } |
| |
| //FIXME this is temporary until we get the tags/175 sorted out |
| private JAnnotation getAnnotation(JAnnotatedElement e, |
| String named) { |
| JAnnotation[] tags = e.getAllJavadocTags(); |
| for(int i=0; i<tags.length; i++) { |
| if (tags[i].getSimpleName().equals(named)) return tags[i]; |
| } |
| return null; |
| } |
| |
| /** |
| * Returns a QName for the type bound to the given JClass. |
| */ |
| private QName getQnameFor(JClass clazz) { |
| getBindingTypeFor(clazz); //ensure that we've bound it |
| JavaTypeName jtn = JavaTypeName.forString(clazz.getQualifiedName()); |
| BindingTypeName btn = mLoader.lookupTypeFor(jtn); |
| logVerbose("BindingTypeName is "+btn,clazz); |
| BindingType bt = mLoader.getBindingType(btn); |
| if (bt != null) return bt.getName().getXmlName().getQName(); |
| logError("could not get qname",clazz); |
| return new QName("ERROR",clazz.getQualifiedName()); |
| } |
| |
| /** |
| * Returns a target namespace that should be used for the given class. |
| * This takes annotations into consideration. |
| */ |
| private String getTargetNamespace(JClass clazz) { |
| String val = getAnnotation(clazz,TAG_CT_TARGETNS,null); |
| if (val != null) return val; |
| // Ok, they didn't specify it in the markup, so we have to |
| // synthesize it from the classname. |
| String pkg_name; |
| if (clazz.isPrimitiveType()) { |
| pkg_name = JAVA_NAMESPACE_URI; |
| } else { |
| JPackage pkg = clazz.getContainingPackage(); |
| pkg_name = (pkg == null) ? "" : pkg.getQualifiedName(); |
| if (pkg_name.startsWith(JAVA_PACKAGE_PREFIX)) { |
| pkg_name = JAVA_NAMESPACE_URI+'.'+ |
| pkg_name.substring(JAVA_PACKAGE_PREFIX.length()); |
| } |
| } |
| return JAVA_URI_SCHEME + pkg_name; |
| } |
| |
| |
| |
| /** |
| * Checks the given XML name to ensure that it is a valid XMLName according |
| * to the XML 1.0 Recommendation. If it is not, the name is mangled so |
| * as to make it a valid name. This should be called before setting the |
| * name on every schema fragment we create. |
| */ |
| private static String makeNcNameSafe(String name) { |
| // it's probably pretty rare that a name isn't valid, so let's just do |
| // an optimistic check first without writing out a new string. |
| if (name == null || XMLChar.isValidNCName(name) || name.length() == 0) { |
| return name; |
| } |
| // ok, we have to mangle it |
| StringWriter out = new StringWriter(); |
| char ch = name.charAt(0); |
| if(!XMLChar.isNCNameStart(ch)) { |
| out.write(SAFE_CHAR); |
| } else { |
| out.write(ch); |
| } |
| for (int i=1; i < name.length(); i++ ) { |
| ch = name.charAt(i); |
| if (!XMLChar.isNCName(ch)) { |
| out.write(ch); |
| } |
| } |
| return out.toString(); |
| } |
| |
| /* |
| |
| private static boolean isXmlObj(JClass clazz) { |
| try { |
| JClass xmlObj = clazz.forName("org.apache.xmlbeans.XmlObject"); |
| return xmlObj.isAssignableFrom(clazz); |
| } catch(Exception e) { |
| e.printStackTrace(); //FIXME |
| return false; |
| } |
| } |
| */ |
| |
| |
| |
| /** |
| * Inner class which encapsulates the creation of schema properties and |
| * property bindings and presents them as a unified interface, a kind of |
| * 'virtual property.' This is used by the bindProperties() method, and |
| * allows that function to concentrate on inspecting the java types and |
| * annotations. This class hides all of the dirty work associated with |
| * constructing and initializing a BTS property and either an XSD element |
| * or attribute. |
| * |
| * Note that in some sense, this class behaves as both a factory and a kind |
| * of cursor. It is capable of creating a new virtual property |
| * on a given BTS/XSD type pair, and any operations on the facade will |
| * apply to that property until the next property is created |
| * (via newAttributeProperty or newElementProperty). |
| * |
| * This class really wouldn't be necessary if the SchemaDocument model |
| * were a bit more user-friendly. |
| */ |
| class SchemaPropertyFacade { |
| |
| // ======================================================================= |
| // Variables |
| |
| private TopLevelComplexType mXsType; |
| private ExtensionType mExtensionType = null; |
| private String mXsTargetNamespace; |
| private LocalElement mXsElement = null; // exactly one of these two is |
| private Attribute mXsAttribute = null; // remains null |
| private Group mXsSequence = null; |
| private List mXsAttributeList = null; |
| private ByNameBean mBtsType; |
| private QNameProperty mBtsProp = null; |
| private JElement mSrcContext = null; |
| |
| // ======================================================================= |
| // Constructors |
| |
| public SchemaPropertyFacade(TopLevelComplexType xsType, |
| ExtensionType extType, //may be null |
| ByNameBean bt, |
| String tns) { |
| if (xsType == null) throw new IllegalArgumentException("null xsType"); |
| if (bt == null) throw new IllegalArgumentException("null bt"); |
| if (tns == null) throw new IllegalArgumentException("null tns"); |
| mXsType = xsType; |
| mExtensionType = extType; |
| mBtsType = bt; |
| mXsTargetNamespace = tns; |
| } |
| |
| // ======================================================================= |
| // Public methods |
| |
| /** |
| * Creates a new element property and sets this facade represent it. |
| * Note that either this method or newAttributeProperty must be called prior |
| * to doing any work with the facade. Also note that you need to |
| * completely finish working with each property before moving onto |
| * the next via newElementProperty or newAttributeProperty. |
| * * |
| * @param srcContext A JAM element that represents the java source |
| * artifact that is being bound to the property. This is used |
| * only for error reporting purposes. |
| */ |
| public void newElementProperty(JElement srcContext) { |
| newBtsProperty(); |
| mSrcContext = srcContext; |
| if (mXsSequence == null) { |
| // nest it inside the extension element if they specified one, |
| // otherwise just do it in the complexType |
| mXsSequence = (mExtensionType != null) ? |
| mExtensionType.addNewSequence() : mXsType.addNewSequence(); |
| } |
| mXsElement = mXsSequence.addNewElement(); |
| mXsAttribute = null; |
| } |
| |
| /** |
| * Creates a new attribute property and sets this facade represent it. |
| * Note that either this method or newElementProperty must be called prior |
| * to doing any work with the facade. Also note that you need to |
| * completely finish working with each property before moving onto |
| * the next via newElementProperty or newAttributeProperty. |
| * |
| * @param srcContext A JAM element that represents the java source |
| * artifact that is being bound to the property. This is used |
| * only for error reporting purposes. |
| */ |
| public void newAttributeProperty(JElement srcContext) { |
| newBtsProperty(); |
| mBtsProp.setAttribute(true); |
| mSrcContext = srcContext; |
| mXsElement = null; |
| if (mXsAttributeList == null) mXsAttributeList = new ArrayList(); |
| mXsAttributeList.add(mXsAttribute = Attribute.Factory.newInstance()); |
| } |
| |
| /** |
| * Sets the name of this property (element or attribute) in the |
| * generated schema. |
| */ |
| public void setSchemaName(String name) { |
| name = makeNcNameSafe(name); |
| if (mXsElement != null) { |
| mXsElement.setName(name); |
| } else if (mXsAttribute != null) { |
| mXsAttribute.setName(name); |
| } else { |
| throw new IllegalStateException(); |
| } |
| mBtsProp.setQName(new QName(mXsTargetNamespace,name)); |
| } |
| |
| /** |
| * Sets the name of the java getter for this property. |
| */ |
| public void setGetter(JMethod g) { |
| mBtsProp.setGetterName(MethodName.create(g)); |
| } |
| |
| /** |
| * Sets the name of the java setter for this property. |
| */ |
| public void setSetter(JMethod s) { |
| mBtsProp.setSetterName(MethodName.create(s)); |
| } |
| |
| /** |
| * Sets the name of the java setter for this property. |
| */ |
| public void setIssetter(JMethod s) { |
| mBtsProp.setIssetterName(MethodName.create(s)); |
| } |
| |
| /** |
| * Sets the type of the property. Currently handles arrays |
| * correctly but not collections. |
| */ |
| public void setType(JClass propType) { |
| if (mXsElement != null) { |
| if (propType.isArrayType()) { |
| if (propType.getArrayDimensions() != 1) { |
| logError("Multidimensional arrays NYI",mSrcContext); //FIXME |
| } |
| JClass componentType = propType.getArrayComponentType(); |
| mXsElement.setMaxOccurs("unbounded"); |
| mXsElement.setType(getQnameFor(componentType)); |
| mBtsProp.setMultiple(true); |
| mBtsProp.setCollectionClass //FIXME |
| (JavaTypeName.forString(componentType.getQualifiedName()+"[]")); |
| mBtsProp.setBindingType(getBindingTypeFor(componentType)); |
| } else { |
| mXsElement.setType(getQnameFor(propType)); |
| mBtsProp.setBindingType(getBindingTypeFor(propType)); |
| } |
| } else if (mXsAttribute != null) { |
| if (propType.isArrayType()) { |
| logError("Array properties cannot be mapped to xml attributes", |
| mSrcContext); |
| } else { |
| mXsAttribute.setType(getQnameFor(propType)); |
| mBtsProp.setBindingType(getBindingTypeFor(propType)); |
| } |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| /** |
| * Sets whether the property should be bound as nillable. |
| */ |
| public void setNillable(boolean b) { |
| if (mXsElement != null) { |
| mXsElement.setNillable(b); |
| mBtsProp.setNillable(b); |
| } else if (mXsAttribute != null) { |
| logError("Attributes cannot be nillable:",mSrcContext); |
| } else { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| /** |
| * This method should always be called when finished building up |
| * a type. It is a hack around an xbeans bug in which the sequences and |
| * attributes are output in the order in which they were added (the |
| * schema for schemas says the attributes always have to go last). |
| */ |
| public void finish() { |
| addBtsProperty(); |
| if (mXsAttributeList != null) { |
| Attribute[] array = new Attribute[mXsAttributeList.size()]; |
| mXsAttributeList.toArray(array); |
| mXsType.setAttributeArray(array); |
| } |
| } |
| |
| // ======================================================================= |
| // Private methods |
| |
| /** |
| * Adds the current bts property to the bts type. This has to be called |
| * for every property. We do this last because ByNameBean won't |
| * let us add more than one prop for same name (name is always blank |
| * initially). |
| */ |
| private void addBtsProperty() { |
| if (mBtsProp != null && !mBtsType.hasProperty(mBtsProp)) |
| mBtsType.addProperty(mBtsProp); |
| } |
| |
| /** |
| * Initialize a new QName property in the bts type |
| */ |
| private void newBtsProperty() { |
| if (mBtsProp != null) addBtsProperty(); //if not 1st one, add old one |
| mBtsProp = new QNameProperty(); |
| } |
| } |
| |
| |
| //this is temporary, will go away when we have our 175 story straight |
| private static JAnnotation[] getNamedTags(JAnnotation[] tags, |
| String named) |
| { |
| if (tags == null || tags.length == 0) return new JAnnotation[0]; |
| List list = new ArrayList(); |
| for(int i=0; i<tags.length; i++) { |
| if (tags[i].getSimpleName().equals(named)) list.add(tags[i]); |
| } |
| JAnnotation[] out = new JAnnotation[list.size()]; |
| list.toArray(out); |
| return out; |
| } |
| |
| |
| |
| } |