Home » Castor-1.3-src » org.exolab.castor » tools » [javadoc | source]

    1   /**
    2    * Redistribution and use of this software and associated documentation
    3    * ("Software"), with or without modification, are permitted provided
    4    * that the following conditions are met:
    5    *
    6    * 1. Redistributions of source code must retain copyright
    7    *    statements and notices.  Redistributions must also contain a
    8    *    copy of this document.
    9    *
   10    * 2. Redistributions in binary form must reproduce the
   11    *    above copyright notice, this list of conditions and the
   12    *    following disclaimer in the documentation and/or other
   13    *    materials provided with the distribution.
   14    *
   15    * 3. The name "Exolab" must not be used to endorse or promote
   16    *    products derived from this Software without prior written
   17    *    permission of Intalio, Inc.  For written permission,
   18    *    please contact info@exolab.org.
   19    *
   20    * 4. Products derived from this Software may not be called "Exolab"
   21    *    nor may "Exolab" appear in their names without prior written
   22    *    permission of Intalio, Inc. Exolab is a registered
   23    *    trademark of Intalio, Inc.
   24    *
   25    * 5. Due credit should be given to the Exolab Project
   26    *    (http://www.exolab.org/).
   27    *
   28    * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
   29    * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
   30    * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   31    * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
   32    * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
   33    * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   34    * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   35    * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   36    * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
   37    * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   38    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
   39    * OF THE POSSIBILITY OF SUCH DAMAGE.
   40    *
   41    * Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
   42    *
   43    * $Id: MappingTool.java 7273 2007-10-04 15:15:24Z wguttmn $
   44    */
   45   package org.exolab.castor.tools;
   46   
   47   import java.io.File;
   48   import java.io.FileWriter;
   49   import java.io.PrintWriter;
   50   import java.io.Writer;
   51   import java.util.Enumeration;
   52   import java.util.Hashtable;
   53   import java.util.Properties;
   54   
   55   import org.castor.xml.BackwardCompatibilityContext;
   56   import org.castor.xml.InternalContext;
   57   import org.exolab.castor.mapping.FieldDescriptor;
   58   import org.exolab.castor.mapping.MappingException;
   59   import org.exolab.castor.mapping.loader.CollectionHandlers;
   60   import org.exolab.castor.mapping.loader.Types;
   61   import org.exolab.castor.mapping.xml.BindXml;
   62   import org.exolab.castor.mapping.xml.ClassChoice;
   63   import org.exolab.castor.mapping.xml.ClassMapping;
   64   import org.exolab.castor.mapping.xml.FieldMapping;
   65   import org.exolab.castor.mapping.xml.MapTo;
   66   import org.exolab.castor.mapping.xml.MappingRoot;
   67   import org.exolab.castor.mapping.xml.types.BindXmlNodeType;
   68   import org.exolab.castor.mapping.xml.types.FieldMappingCollectionType;
   69   import org.exolab.castor.util.CommandLineOptions;
   70   import org.exolab.castor.util.dialog.ConsoleDialog;
   71   import org.exolab.castor.xml.Marshaller;
   72   import org.exolab.castor.xml.XMLClassDescriptor;
   73   import org.exolab.castor.xml.XMLContext;
   74   import org.exolab.castor.xml.XMLFieldDescriptor;
   75   
   76   /**
   77    * A tool which uses the introspector to automatically
   78    * create mappings for a given set of classes.
   79    *
   80    * @author <a href="arkin@intalio.com">Assaf Arkin</a>
   81    * @author <a href="keith AT kvisco DOT com">Keith Visco</a>
   82    * @version $Revision: 7273 $ $Date: 2006-01-30 14:37:08 -0700 (Mon, 30 Jan 2006) $
   83    */
   84   public class MappingTool {
   85       /** Used for checking field names to see if they begin with an underscore '_'. */
   86       private static final String UNDERSCORE = "_";
   87       
   88       /** Hashtable of already generated mappings. */
   89       private Hashtable _mappings;
   90   
   91       /**
   92        * The internal MappingLoader to use for checking whether or not we can find
   93        * the proper accessor methods.
   94        */
   95       private MappingToolMappingLoader _mappingLoader;
   96   
   97       /**
   98        * Boolean to indicate that we should always perform introspection for each
   99        * class even if a ClassDescriptor may exist.
  100        */
  101       private boolean _forceIntrospection = false;
  102   
  103       /**
  104        * The XMLContext (mother of all dwelling).
  105        */
  106       private InternalContext _internalContext;
  107   
  108       /**
  109        * Constructor, builds up the relations.
  110        */
  111       public MappingTool() {
  112           super();
  113       } // --MappingTool
  114   
  115       /**
  116        * Command line method.
  117        * 
  118        * @param args
  119        *            the command line parameters
  120        */
  121       public static void main(final String[] args) {
  122           CommandLineOptions allOptions = new CommandLineOptions();
  123   
  124           // -- Input classname flag
  125           allOptions.addFlag("i", "classname", "Sets the input class");
  126   
  127           // -- Output filename flag
  128           String desc = "Sets the output mapping filename";
  129           allOptions.addFlag("o", "filename", desc, true);
  130   
  131           // -- Force flag
  132           desc = "Force overwriting of files.";
  133           allOptions.addFlag("f", "", desc, true);
  134   
  135           // -- Help flag
  136           desc = "Displays this help screen.";
  137           allOptions.addFlag("h", "", desc, true);
  138   
  139           // -- Process the specified command line options
  140           Properties options = allOptions.getOptions(args);
  141   
  142           // -- check for help option
  143           if (options.getProperty("h") != null) {
  144               PrintWriter pw = new PrintWriter(System.out, true);
  145               allOptions.printHelp(pw);
  146               pw.flush();
  147               return;
  148           }
  149   
  150           String classname = options.getProperty("i");
  151           String mappingName = options.getProperty("o");
  152           boolean force = (options.getProperty("f") != null);
  153   
  154           if (classname == null) {
  155               PrintWriter pw = new PrintWriter(System.out, true);
  156               allOptions.printUsage(pw);
  157               pw.flush();
  158               return;
  159           }
  160   
  161           MappingTool tool;
  162   
  163           try {
  164               XMLContext xmlContext = new XMLContext();
  165               tool = xmlContext.createMappingTool();
  166               tool.addClass(classname);
  167   
  168               Writer writer = null;
  169   
  170               if ((mappingName == null) || (mappingName.length() == 0)) {
  171                   writer = new PrintWriter(System.out, true);
  172               } else {
  173                   File file = new File(mappingName);
  174                   if (file.exists() && (!force)) {
  175                       ConsoleDialog dialog = new ConsoleDialog();
  176                       String message = "The file already exists. Do you wish " + "to overwrite '"
  177                               + mappingName + "'?";
  178                       if (!dialog.confirm(message)) {
  179                           return;
  180                       }
  181                   }
  182                   writer = new FileWriter(file);
  183               }
  184   
  185               tool.write(writer);
  186           } catch (Exception except) {
  187               System.out.println(except);
  188               except.printStackTrace();
  189           }
  190       } // -- main
  191   
  192       /**
  193        * Adds the Class, specified by the given name, to the mapping file.
  194        * 
  195        * @param name
  196        *            the name of the Class to add
  197        * @throws MappingException
  198        *             in case that the name is null or the Class can not be loaded
  199        */
  200       public void addClass(final String name) throws MappingException {
  201           addClass(name, true);
  202       } // -- addClass
  203   
  204       /**
  205        * Adds the Class, specified by the given name, to the mapping file.
  206        * 
  207        * @param name
  208        *            the name of the Class to add
  209        * @param deep
  210        *            a flag to indicate that recursive processing should take place
  211        *            and all classes used by the given class should also be added
  212        *            to the mapping file. This flag is true by default.
  213        * @throws MappingException
  214        *             in case that the name is null or the Class can not be loaded
  215        */
  216       public void addClass(final String name, final boolean deep) throws MappingException {
  217           if (name == null) {
  218               throw new MappingException("Cannot introspect a null class.");
  219           }
  220   
  221           try {
  222               addClass(Class.forName(name), deep);
  223           } catch (ClassNotFoundException except) {
  224               throw new MappingException(except);
  225           }
  226       } // -- addClass
  227   
  228       /**
  229        * Adds the given Class to the mapping file.
  230        * 
  231        * @param cls
  232        *            the Class to add
  233        * @throws MappingException
  234        *             in case that the name is null or the Class can not be loaded
  235        */
  236       public void addClass(final Class cls) throws MappingException {
  237           addClass(cls, true);
  238       } // -- addClass
  239   
  240       /**
  241        * Adds the given Class to the mapping file. If the deep flag is true, all
  242        * mappings for Classes used by the given Class will also be added to the
  243        * mapping file.
  244        * 
  245        * @param cls
  246        *            the Class to add
  247        * @param deep
  248        *            a flag to indicate that recursive processing should take place
  249        *            and all classes used by the given class should also be added
  250        *            to the mapping file. This flag is true by default.
  251        * @throws MappingException
  252        *             in case that the name is null or the Class can not be loaded
  253        */
  254       public void addClass(final Class cls, final boolean deep) throws MappingException {
  255           if (cls == null) {
  256               throw new MappingException("Cannot introspect a null class.");
  257           }
  258   
  259           if (_mappings.get(cls) != null) {
  260               return;
  261           }
  262   
  263           if (cls.isArray()) {
  264               Class cType = cls.getComponentType();
  265               if (_mappings.get(cType) != null) {
  266                   return;
  267               }
  268               if (Types.isSimpleType(cType)) {
  269                   return;
  270               }
  271               // -- handle component type
  272               addClass(cType);
  273           }
  274   
  275           if (_forceIntrospection && (!Types.isConstructable(cls))) {
  276               throw new MappingException("mapping.classNotConstructable", cls.getName());
  277           }
  278   
  279           XMLClassDescriptor xmlClass;
  280           FieldDescriptor[] fields;
  281           ClassMapping classMap;
  282           FieldMapping fieldMap;
  283   
  284           boolean introspected = false;
  285           try {
  286               if (_forceIntrospection) {
  287                   xmlClass = _internalContext.getIntrospector().generateClassDescriptor(cls);
  288                   introspected = true;
  289               } else {
  290                   xmlClass = (XMLClassDescriptor) _internalContext.getXMLClassDescriptorResolver().resolve(cls);
  291                   introspected = _internalContext.getIntrospector().introspected(xmlClass);
  292               }
  293           } catch (Exception except) {
  294               throw new MappingException(except);
  295           }
  296           classMap = new ClassMapping();
  297           classMap.setName(cls.getName());
  298           classMap.setDescription("Default mapping for class " + cls.getName());
  299   
  300           // -- prevent default access from showing up in the mapping
  301           classMap.setAccess(null);
  302   
  303           // -- map-to
  304           MapTo mapTo = new MapTo();
  305           mapTo.setXml(xmlClass.getXMLName());
  306           mapTo.setNsUri(xmlClass.getNameSpaceURI());
  307           mapTo.setNsPrefix(xmlClass.getNameSpacePrefix());
  308           classMap.setMapTo(mapTo);
  309   
  310           // -- add mapping to hashtable before processing
  311           // -- fields so we can do recursive processing
  312           _mappings.put(cls, classMap);
  313   
  314           fields = xmlClass.getFields();
  315           for (int i = 0; i < fields.length; ++i) {
  316               FieldDescriptor fdesc = fields[i];
  317   
  318               String fieldName = fdesc.getFieldName();
  319   
  320               boolean isContainer = false;
  321               // -- check for collection wrapper
  322               if (introspected && fieldName.startsWith("##container")) {
  323                   fdesc = fdesc.getClassDescriptor().getFields()[0];
  324                   fieldName = fdesc.getFieldName();
  325                   isContainer = true;
  326               }
  327   
  328               Class fieldType = fdesc.getFieldType();
  329   
  330               // -- check to make sure we can find the accessors...
  331               // -- if we used introspection we don't need to
  332               // -- enter this block...only when descriptors
  333               // -- were generated using the source code generator
  334               // -- or by hand.
  335               if ((!introspected) && fieldName.startsWith(UNDERSCORE)) {
  336                   // -- check to see if we need to remove underscore
  337                   if (!_mappingLoader.canFindAccessors(cls, fieldName, fieldType)) {
  338                       fieldName = fieldName.substring(1);
  339                   }
  340   
  341                   // -- check to see if we need to remove "List" prefix
  342                   // -- used by generated source code
  343                   if (!_mappingLoader.canFindAccessors(cls, fieldName, fieldType)) {
  344                       if (fieldName.endsWith("List")) {
  345                           int len = fieldName.length() - 4;
  346                           String tmpName = fieldName.substring(0, len);
  347                           if (_mappingLoader.canFindAccessors(cls, tmpName, fieldType)) {
  348                               fieldName = tmpName;
  349                           }
  350                       }
  351                   }
  352               }
  353   
  354               fieldMap = new FieldMapping();
  355               fieldMap.setName(fieldName);
  356   
  357               // -- unwrap arrays of objects
  358               boolean isArray = fieldType.isArray();
  359               while (fieldType.isArray()) {
  360                   fieldType = fieldType.getComponentType();
  361               }
  362   
  363               // -- To prevent outputing of optional fields...check
  364               // -- for value first before setting
  365               if (fdesc.isRequired()) {
  366                   fieldMap.setRequired(true);
  367               }
  368               if (fdesc.isTransient()) {
  369                   fieldMap.setTransient(true);
  370               }
  371               if (fdesc.isMultivalued()) {
  372                   // -- special case for collections
  373                   if (isContainer) {
  374                       // -- backwards than what you'd expect, but
  375                       // -- if the collection had a "container" wrapper
  376                       // -- then we specify container="false" in the
  377                       // -- mapping file.
  378                       fieldMap.setContainer(false);
  379                   }
  380   
  381                   // -- try to guess collection type
  382                   if (isArray) {
  383                       fieldMap.setCollection(FieldMappingCollectionType.ARRAY);
  384                   } else {
  385                       // -- if the fieldType is the collection, then set
  386                       // appropriate
  387                       // -- collection type
  388                       String colName = CollectionHandlers.getCollectionName(fieldType);
  389                       if (colName != null) {
  390                           fieldMap.setCollection(FieldMappingCollectionType.valueOf(colName));
  391                           fieldType = Object.class;
  392                       } else if (_mappingLoader.returnsArray(cls, fieldName, fieldType)) {
  393                           // -- help maintain compatibility with generated
  394                           // descriptors
  395                           fieldMap.setCollection(FieldMappingCollectionType.ARRAY);
  396                       } else {
  397                           fieldMap.setCollection(FieldMappingCollectionType.ENUMERATE);
  398                       }
  399                   }
  400               }
  401   
  402               // -- fieldType
  403               fieldMap.setType(fieldType.getName());
  404   
  405               // -- handle XML Specific information
  406               fieldMap.setBindXml(new BindXml());
  407               fieldMap.getBindXml().setName(((XMLFieldDescriptor) fdesc).getXMLName());
  408               fieldMap.getBindXml().setNode(
  409                       BindXmlNodeType.valueOf(((XMLFieldDescriptor) fields[i]).getNodeType()
  410                               .toString()));
  411               if (classMap.getClassChoice() == null) {
  412                   classMap.setClassChoice(new ClassChoice());
  413               }
  414               classMap.getClassChoice().addFieldMapping(fieldMap);
  415   
  416               if (deep) {
  417                   if (_mappings.get(fieldType) != null) {
  418                       continue;
  419                   }
  420                   if (Types.isSimpleType(fieldType)) {
  421                       continue;
  422                   }
  423                   // -- recursive add needed classes
  424                   addClass(fieldType);
  425               }
  426           }
  427       } // -- addClass
  428   
  429       /**
  430        * Enables or disables the forcing of introspection when a ClassDescriptor
  431        * already exists. This is false by default.
  432        * 
  433        * @param force
  434        *            when true will cause the MappingTool to always use
  435        *            introspection regardless of whether or not a ClassDescriptor
  436        *            exists for a given Class.
  437        */
  438       public void setForceIntrospection(final boolean force) {
  439           _forceIntrospection = force;
  440       } // -- setForceInstrospection
  441   
  442       /**
  443        * Serializes the mapping to the given writer.
  444        * 
  445        * @param writer
  446        *            the Writer to serialize the mapping to
  447        * @throws MappingException if writing the mapping information fails
  448        */
  449       public void write(final Writer writer) throws MappingException {
  450           Marshaller marshal;
  451           MappingRoot mapping;
  452           Enumeration enumeration;
  453   
  454           try {
  455               mapping = new MappingRoot();
  456               mapping.setDescription("Castor generated mapping file");
  457               enumeration = _mappings.elements();
  458               while (enumeration.hasMoreElements()) {
  459                   mapping.addClassMapping((ClassMapping) enumeration.nextElement());
  460               }
  461               marshal = new Marshaller(writer);
  462               marshal.setNamespaceMapping(null, "http://castor.exolab.org/");
  463               marshal.setNamespaceMapping("cst", "http://castor.exolab.org/");
  464               marshal.marshal(mapping);
  465           } catch (Exception except) {
  466               throw new MappingException(except);
  467           }
  468       } // -- write
  469   
  470       /**
  471        * To set the XMLContext to be used.
  472        * @param internalContext the XMLContext to be used
  473        */
  474       public void setInternalContext(final InternalContext internalContext) {
  475           _internalContext = internalContext;
  476           _mappings = new Hashtable();
  477           _mappingLoader = new MappingToolMappingLoader(_internalContext.getJavaNaming());
  478       }
  479   } // -- MappingTool

Home » Castor-1.3-src » org.exolab.castor » tools » [javadoc | source]