org.exolab.castor.tools.MappingTool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of castor-xml Show documentation
Show all versions of castor-xml Show documentation
The core XML data binding framework with support for marshalling Java objects to
and unmarshalling from XML documents.
The newest version!
/**
* Redistribution and use of this software and associated documentation ("Software"), with or
* without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright statements and notices. Redistributions
* must also contain a copy of this document.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided with
* the distribution.
*
* 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
* without prior written permission of Intalio, Inc. For written permission, please contact
* [email protected].
*
* 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
* their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
* Intalio, Inc.
*
* 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
*
* THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
*
* $Id$
*/
package org.exolab.castor.tools;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import org.castor.xml.BackwardCompatibilityContext;
import org.castor.xml.InternalContext;
import org.exolab.castor.mapping.FieldDescriptor;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.mapping.loader.CollectionHandlers;
import org.exolab.castor.mapping.loader.Types;
import org.exolab.castor.mapping.xml.BindXml;
import org.exolab.castor.mapping.xml.ClassChoice;
import org.exolab.castor.mapping.xml.ClassMapping;
import org.exolab.castor.mapping.xml.FieldMapping;
import org.exolab.castor.mapping.xml.MapTo;
import org.exolab.castor.mapping.xml.MappingRoot;
import org.exolab.castor.mapping.xml.types.BindXmlNodeType;
import org.exolab.castor.mapping.xml.types.FieldMappingCollectionType;
import org.exolab.castor.util.CommandLineOptions;
import org.exolab.castor.util.dialog.ConsoleDialog;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.XMLClassDescriptor;
import org.exolab.castor.xml.XMLContext;
import org.exolab.castor.xml.XMLFieldDescriptor;
/**
* A tool which uses the introspector to automatically create mappings for a given set of classes.
*
* @author Assaf Arkin
* @author Keith Visco
* @version $Revision$ $Date: 2006-01-30 14:37:08 -0700 (Mon, 30 Jan 2006) $
*/
public class MappingTool {
/** Used for checking field names to see if they begin with an underscore '_'. */
private static final String UNDERSCORE = "_";
/** Hashtable of already generated mappings. */
private Hashtable _mappings;
/**
* The internal MappingLoader to use for checking whether or not we can find the proper accessor
* methods.
*/
private MappingToolMappingLoader _mappingLoader;
/**
* Boolean to indicate that we should always perform introspection for each class even if a
* ClassDescriptor may exist.
*/
private boolean _forceIntrospection = false;
/**
* The XMLContext (mother of all dwelling).
*/
private InternalContext _internalContext;
/**
* Constructor, builds up the relations.
*/
public MappingTool() {
super();
} // --MappingTool
/**
* Command line method.
*
* @param args the command line parameters
*/
public static void main(final String[] args) {
CommandLineOptions allOptions = new CommandLineOptions();
// -- Input classname flag
allOptions.addFlag("i", "classname", "Sets the input class");
// -- Output filename flag
String desc = "Sets the output mapping filename";
allOptions.addFlag("o", "filename", desc, true);
// -- Force flag
desc = "Force overwriting of files.";
allOptions.addFlag("f", "", desc, true);
// -- Help flag
desc = "Displays this help screen.";
allOptions.addFlag("h", "", desc, true);
// -- Process the specified command line options
Properties options = allOptions.getOptions(args);
// -- check for help option
if (options.getProperty("h") != null) {
PrintWriter pw = new PrintWriter(System.out, true);
allOptions.printHelp(pw);
pw.flush();
return;
}
String classname = options.getProperty("i");
String mappingName = options.getProperty("o");
boolean force = (options.getProperty("f") != null);
if (classname == null) {
PrintWriter pw = new PrintWriter(System.out, true);
allOptions.printUsage(pw);
pw.flush();
return;
}
MappingTool tool;
try {
XMLContext xmlContext = new XMLContext();
tool = xmlContext.createMappingTool();
tool.addClass(classname);
Writer writer = null;
if ((mappingName == null) || (mappingName.length() == 0)) {
writer = new PrintWriter(System.out, true);
} else {
File file = new File(mappingName);
if (file.exists() && (!force)) {
ConsoleDialog dialog = new ConsoleDialog();
String message =
"The file already exists. Do you wish " + "to overwrite '" + mappingName + "'?";
if (!dialog.confirm(message)) {
return;
}
}
writer = new FileWriter(file);
}
tool.write(writer);
} catch (Exception except) {
System.out.println(except);
except.printStackTrace();
}
} // -- main
/**
* Adds the Class, specified by the given name, to the mapping file.
*
* @param name the name of the Class to add
* @throws MappingException in case that the name is null or the Class can not be loaded
*/
public void addClass(final String name) throws MappingException {
addClass(name, true);
} // -- addClass
/**
* Adds the Class, specified by the given name, to the mapping file.
*
* @param name the name of the Class to add
* @param deep a flag to indicate that recursive processing should take place and all classes used
* by the given class should also be added to the mapping file. This flag is true by
* default.
* @throws MappingException in case that the name is null or the Class can not be loaded
*/
public void addClass(final String name, final boolean deep) throws MappingException {
if (name == null) {
throw new MappingException("Cannot introspect a null class.");
}
try {
addClass(Class.forName(name), deep);
} catch (ClassNotFoundException except) {
throw new MappingException(except);
}
} // -- addClass
/**
* Adds the given Class to the mapping file.
*
* @param cls the Class to add
* @throws MappingException in case that the name is null or the Class can not be loaded
*/
public void addClass(final Class cls) throws MappingException {
addClass(cls, true);
} // -- addClass
/**
* Adds the given Class to the mapping file. If the deep flag is true, all mappings for Classes
* used by the given Class will also be added to the mapping file.
*
* @param cls the Class to add
* @param deep a flag to indicate that recursive processing should take place and all classes used
* by the given class should also be added to the mapping file. This flag is true by
* default.
* @throws MappingException in case that the name is null or the Class can not be loaded
*/
public void addClass(final Class cls, final boolean deep) throws MappingException {
if (cls == null) {
throw new MappingException("Cannot introspect a null class.");
}
if (_mappings.get(cls) != null) {
return;
}
if (cls.isArray()) {
Class cType = cls.getComponentType();
if (_mappings.get(cType) != null) {
return;
}
if (Types.isSimpleType(cType)) {
return;
}
// -- handle component type
addClass(cType);
}
if (_forceIntrospection && (!Types.isConstructable(cls))) {
throw new MappingException("mapping.classNotConstructable", cls.getName());
}
XMLClassDescriptor xmlClass;
FieldDescriptor[] fields;
ClassMapping classMap;
FieldMapping fieldMap;
boolean introspected = false;
try {
if (_forceIntrospection) {
xmlClass = _internalContext.getIntrospector().generateClassDescriptor(cls);
introspected = true;
} else {
xmlClass =
(XMLClassDescriptor) _internalContext.getXMLClassDescriptorResolver().resolve(cls);
introspected = _internalContext.getIntrospector().introspected(xmlClass);
}
} catch (Exception except) {
throw new MappingException(except);
}
classMap = new ClassMapping();
classMap.setName(cls.getName());
classMap.setDescription("Default mapping for class " + cls.getName());
// -- prevent default access from showing up in the mapping
classMap.setAccess(null);
// -- map-to
MapTo mapTo = new MapTo();
mapTo.setXml(xmlClass.getXMLName());
mapTo.setNsUri(xmlClass.getNameSpaceURI());
mapTo.setNsPrefix(xmlClass.getNameSpacePrefix());
classMap.setMapTo(mapTo);
// -- add mapping to hashtable before processing
// -- fields so we can do recursive processing
_mappings.put(cls, classMap);
fields = xmlClass.getFields();
for (int i = 0; i < fields.length; ++i) {
FieldDescriptor fdesc = fields[i];
String fieldName = fdesc.getFieldName();
boolean isContainer = false;
// -- check for collection wrapper
if (introspected && fieldName.startsWith("##container")) {
fdesc = fdesc.getClassDescriptor().getFields()[0];
fieldName = fdesc.getFieldName();
isContainer = true;
}
Class fieldType = fdesc.getFieldType();
// -- check to make sure we can find the accessors...
// -- if we used introspection we don't need to
// -- enter this block...only when descriptors
// -- were generated using the source code generator
// -- or by hand.
if ((!introspected) && fieldName.startsWith(UNDERSCORE)) {
// -- check to see if we need to remove underscore
if (!_mappingLoader.canFindAccessors(cls, fieldName, fieldType)) {
fieldName = fieldName.substring(1);
}
// -- check to see if we need to remove "List" prefix
// -- used by generated source code
if (!_mappingLoader.canFindAccessors(cls, fieldName, fieldType)) {
if (fieldName.endsWith("List")) {
int len = fieldName.length() - 4;
String tmpName = fieldName.substring(0, len);
if (_mappingLoader.canFindAccessors(cls, tmpName, fieldType)) {
fieldName = tmpName;
}
}
}
}
fieldMap = new FieldMapping();
fieldMap.setName(fieldName);
// -- unwrap arrays of objects
boolean isArray = fieldType.isArray();
while (fieldType.isArray()) {
fieldType = fieldType.getComponentType();
}
// -- To prevent outputing of optional fields...check
// -- for value first before setting
if (fdesc.isRequired()) {
fieldMap.setRequired(true);
}
if (fdesc.isTransient()) {
fieldMap.setTransient(true);
}
if (fdesc.isMultivalued()) {
// -- special case for collections
if (isContainer) {
// -- backwards than what you'd expect, but
// -- if the collection had a "container" wrapper
// -- then we specify container="false" in the
// -- mapping file.
fieldMap.setContainer(false);
}
// -- try to guess collection type
if (isArray) {
fieldMap.setCollection(FieldMappingCollectionType.ARRAY);
} else {
// -- if the fieldType is the collection, then set
// appropriate
// -- collection type
String colName = CollectionHandlers.getCollectionName(fieldType);
if (colName != null) {
fieldMap.setCollection(FieldMappingCollectionType.valueOf(colName));
fieldType = Object.class;
} else if (_mappingLoader.returnsArray(cls, fieldName, fieldType)) {
// -- help maintain compatibility with generated
// descriptors
fieldMap.setCollection(FieldMappingCollectionType.ARRAY);
} else {
fieldMap.setCollection(FieldMappingCollectionType.ENUMERATE);
}
}
}
// -- fieldType
fieldMap.setType(fieldType.getName());
// -- handle XML Specific information
fieldMap.setBindXml(new BindXml());
fieldMap.getBindXml().setName(((XMLFieldDescriptor) fdesc).getXMLName());
fieldMap.getBindXml().setNode(
BindXmlNodeType.valueOf(((XMLFieldDescriptor) fields[i]).getNodeType().toString()));
if (classMap.getClassChoice() == null) {
classMap.setClassChoice(new ClassChoice());
}
classMap.getClassChoice().addFieldMapping(fieldMap);
if (deep) {
if (_mappings.get(fieldType) != null) {
continue;
}
if (Types.isSimpleType(fieldType)) {
continue;
}
// -- recursive add needed classes
addClass(fieldType);
}
}
} // -- addClass
/**
* Enables or disables the forcing of introspection when a ClassDescriptor already exists. This is
* false by default.
*
* @param force when true will cause the MappingTool to always use introspection regardless of
* whether or not a ClassDescriptor exists for a given Class.
*/
public void setForceIntrospection(final boolean force) {
_forceIntrospection = force;
} // -- setForceInstrospection
/**
* Serializes the mapping to the given writer.
*
* @param writer the Writer to serialize the mapping to
* @throws MappingException if writing the mapping information fails
*/
public void write(final Writer writer) throws MappingException {
Marshaller marshal;
MappingRoot mapping;
Enumeration enumeration;
try {
mapping = new MappingRoot();
mapping.setDescription("Castor generated mapping file");
enumeration = _mappings.elements();
while (enumeration.hasMoreElements()) {
mapping.addClassMapping((ClassMapping) enumeration.nextElement());
}
marshal = new Marshaller(writer);
marshal.setNamespaceMapping(null, "http://castor.exolab.org/");
marshal.setNamespaceMapping("cst", "http://castor.exolab.org/");
marshal.marshal(mapping);
} catch (Exception except) {
throw new MappingException(except);
}
} // -- write
/**
* To set the XMLContext to be used.
*
* @param internalContext the XMLContext to be used
*/
public void setInternalContext(final InternalContext internalContext) {
_internalContext = internalContext;
_mappings = new Hashtable();
_mappingLoader = new MappingToolMappingLoader(_internalContext.getJavaNaming());
}
} // -- MappingTool