Save This Page
Home » jdo2-model-2.3-ea-src » org.apache.jdo.impl.model.jdo » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *     http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software 
   12    * distributed under the License is distributed on an "AS IS" BASIS, 
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
   14    * See the License for the specific language governing permissions and 
   15    * limitations under the License.
   16    */
   17   
   18   package org.apache.jdo.impl.model.jdo;
   19   
   20   import java.io.InputStream;
   21   import java.io.InputStreamReader;
   22   import java.io.IOException;
   23   
   24   import java.util.ArrayList;
   25   import java.util.Collection;
   26   import java.util.ConcurrentModificationException;
   27   import java.util.HashMap;
   28   import java.util.HashSet;
   29   import java.util.Iterator;
   30   import java.util.List;
   31   import java.util.Map;
   32   import java.util.NoSuchElementException;
   33   import java.util.Set;
   34   
   35   import javax.xml.parsers.ParserConfigurationException;
   36   
   37   import org.apache.commons.logging.Log;
   38   import org.apache.commons.logging.LogFactory;
   39   
   40   import org.apache.jdo.impl.model.jdo.xml.JDOHandler;
   41   import org.apache.jdo.impl.model.jdo.xml.JDOHandlerImpl;
   42   import org.apache.jdo.impl.model.jdo.xml.JDOParser;
   43   import org.apache.jdo.model.ModelException;
   44   import org.apache.jdo.model.ModelFatalException;
   45   import org.apache.jdo.model.java.JavaModel;
   46   import org.apache.jdo.model.java.JavaType;
   47   import org.apache.jdo.model.jdo.JDOClass;
   48   import org.apache.jdo.model.jdo.JDOModel;
   49   import org.apache.jdo.model.jdo.JDOModelFactory;
   50   import org.apache.jdo.model.jdo.JDOPackage;
   51   
   52   import org.apache.jdo.util.I18NHelper;
   53   import org.apache.jdo.util.StringHelper;
   54   
   55   import org.xml.sax.SAXException;
   56   import org.xml.sax.InputSource;
   57   
   58   /**
   59    * A JDOModel instance bundles a number of JDOClass instances used by an 
   60    * application. It provides factory methods to create and retrieve JDOClass 
   61    * instances. A fully qualified class name must be unique within a JDOModel 
   62    * instance. The model supports multiple classes having the same fully qualified 
   63    * name by different JDOModel instances.
   64    * <p>
   65    * The dynamic JDOModel implementation does not store any internally
   66    * calculated values. It is intended to be used in an environment
   67    * where JDO metatdata are likely to be changed (e.g. at development
   68    * time).
   69    * <br> 
   70    * There are two exceptions:
   71    * <ul>
   72    * <li>JDOModelImplDynamic caches JDOClass instances. This means a
   73    * second lookup of the same class will return the same JDOClass
   74    * instance.
   75    * <li>JDOModelImplDynamic manages a list of xml resources (.jdo
   76    * files) that are processed already, to avoid reading the same
   77    * resource again.
   78    * <p>
   79    * TBD:
   80    * <ul>
   81    * <li> Other implementations of JavaModel interface: Development
   82    * <li> Check synchronization.
   83    * <li> Check non validating XML parsing
   84    * <li> Open issue: a .jdo file might contain XML for multiple classes. 
   85    * Today all the metadata is stored in the same jdoModel instance, but at runtime
   86    * class loading determines the correct jdoModel instance. 
   87    * Either we need to be able to change the declaringModel of a JDOClass instance. 
   88    * Or reading a .jdo only load metadata for the requested class, so all the other 
   89    * metadata is ignored.
   90    * </ul>
   91    *
   92    * @author Michael Bouschen
   93    * @since 1.1
   94    * @version 2.0
   95    */
   96   public class JDOModelImplDynamic extends JDOElementImpl implements JDOModel {
   97   
   98       /** 
   99        * Map of JDOPackage managed by this JDOModel instance,
  100        * key is the package name.
  101        */
  102       private Map jdoPackages = new HashMap();
  103   
  104       /** 
  105        * Map of JDOClass objects managed by this JDOModel instance, 
  106        * key is the fully qualified class name. 
  107        */
  108       private Map jdoClasses = new HashMap();
  109   
  110       /**
  111        * Set of names of XML resources that are processed already 
  112        * (see {link #lookupXMLMetadata(String className)}.
  113        */
  114       private Set processedResources = new HashSet();
  115   
  116       /** The JavaModel used to get type info. */
  117       private JavaModel javaModel;
  118   
  119       /** The default for loadXMLMetadata. */ 
  120       private final boolean loadXMLMetadataDefault;
  121       
  122       /** */
  123       private final UnresolvedRelationshipHelper unresolvedRelationshipHelper = 
  124           new UnresolvedRelationshipHelper();
  125   
  126       /** I18N support */
  127       protected final static I18NHelper msg =
  128           I18NHelper.getInstance(JDOModelImplDynamic.class);
  129   
  130       /** XML Logger */
  131       protected static Log xmlLogger = 
  132           LogFactory.getFactory().getInstance("org.apache.jdo.impl.model.jdo.xml"); // NOI18N
  133   
  134       /** Logger */
  135       protected static Log logger =
  136           LogFactory.getFactory().getInstance("org.apache.jdo.impl.model.jdo"); // NOI18N
  137   
  138       /** 
  139        * Constructor. 
  140        * JDOModel instances are created using the JDOModelFactory only.
  141        */
  142       protected JDOModelImplDynamic(
  143           JavaModel javaModel, boolean loadXMLMetadataDefault) {
  144           super();
  145           setJavaModel(javaModel);
  146           this.loadXMLMetadataDefault = loadXMLMetadataDefault;
  147           try {
  148               javaModel.setJDOModel(this);
  149           }
  150           catch (ModelException ex) {
  151               throw new ModelFatalException(msg.msg("ERR_CannotSetJDOModel"), ex); //NOI18N
  152           }
  153       }
  154   
  155       /** 
  156        * The method returns a JDOClass instance for the specified package name.
  157        * If this JDOModel contains the corresponding JDOPackage instance,
  158        * the existing instance is returned. Otherwise, it creates a new JDOPackage
  159        * instance and returns the new instance.
  160        * @param packageName the name of the JDOPackage instance 
  161        * to be returned
  162        * @return a JDOPackage instance for the specified package name
  163        * @exception ModelException if impossible
  164        */
  165       public JDOPackage createJDOPackage(String packageName) 
  166           throws ModelException {
  167           JDOPackage jdoPackage = getJDOPackage(packageName);
  168           if (jdoPackage == null) {
  169               jdoPackage = new JDOPackageImpl();
  170               jdoPackage.setName(packageName);
  171               jdoPackage.setDeclaringModel(this);
  172               jdoPackages.put(packageName, jdoPackage);
  173           }
  174           return jdoPackage;
  175       }
  176       
  177       /** 
  178        * The method returns the JDOPackage instance for the specified package 
  179        * name, if present. The method returns <code>null</code> if it cannot 
  180        * find a JDOPackage instance for the specified name. 
  181        * @param packageName the name of the JDOPackage instance 
  182        * to be returned
  183        * @return a JDOPackage instance for the specified package name 
  184        * or <code>null</code> if not present
  185        */
  186       public JDOPackage getJDOPackage(String packageName) {
  187           return (JDOPackage)jdoPackages.get(packageName);
  188       }
  189   
  190       /**
  191        * Returns the collection of JDOPackage instances declared by this JDOModel 
  192        * in the format of an array.
  193        * @return the packages declared by this JDOModel
  194        */
  195       public JDOPackage[] getDeclaredPackages() {
  196           return (JDOPackage[])jdoPackages.values().toArray(
  197               new JDOPackage[jdoPackages.size()]);
  198       }
  199   
  200       /**
  201        * The method returns a JDOClass instance for the specified fully qualified
  202        * class name. If this JDOModel contains the corresponding JDOClass instance,
  203        * the existing instance is returned. Otherwise, it creates a new JDOClass 
  204        * instance, sets its declaringModel and returns the new instance.
  205        * <p>
  206        * Whether this method reads XML metatdata or not is determined at
  207        * JDOModel creation time (see flag <code>loadXMLMetadataDefault</code> 
  208        * in {@link JDOModelFactory#getJDOModel(JavaModel javaModel, boolean
  209        * loadXMLMetadataDefault)}). Invoking this method is method is equivalent
  210        * to <code>createJDOClass(className, loadXMLMetadataDefault)</code>.
  211        * @param className the fully qualified class name of the JDOClass
  212        * instance to be returned
  213        * @return a JDOClass instance for the specified class name
  214        * @exception ModelException if impossible
  215        */
  216       public JDOClass createJDOClass(String className) throws ModelException {
  217           return createJDOClass(className, loadXMLMetadataDefault);
  218       }
  219   
  220       /**
  221        * The method returns a JDOClass instance for the specified fully qualified
  222        * class name. If this JDOModel contains the corresponding JDOClass instance,
  223        * the existing instance is returned. Otherwise, if the flag loadXMLMetadata
  224        * is set to <code>true</code> the method tries to find the JDOClass 
  225        * instance by reading the XML metadata. If it could not be found the method
  226        * creates a new JDOClass instance, sets its declaringModel and returns the 
  227        * instance.
  228        * @param className the fully qualified class name of the JDOClass instance 
  229        * to be returned
  230        * @param loadXMLMetadata indicated whether to read XML metadata or not
  231        * @return a JDOClass instance for the specified class name
  232        * @exception ModelException if impossible
  233        */
  234       public synchronized JDOClass createJDOClass(String className, 
  235                                                   boolean loadXMLMetadata)
  236           throws ModelException {
  237           JDOClass jdoClass = getJDOClass(className, loadXMLMetadata);
  238           if (jdoClass == null) {
  239               if (logger.isDebugEnabled())
  240                   logger.debug("JDOModel.createJDOClass: " + //NOI18N
  241                                "create new JDOClass instance " + className); //NOI18N
  242               jdoClass = newJDOClassInstance(className);
  243               jdoClass.setDeclaringModel(this);
  244               jdoClasses.put(className, jdoClass);
  245               // create the corresponding JDOPackage
  246               jdoClass.setJDOPackage(createJDOPackage(getPackageName(className)));
  247           }
  248           return jdoClass;
  249       }
  250   
  251       /**
  252        * The method returns the JDOClass instance for the specified fully 
  253        * qualified class name if present. The method returns <code>null</code> 
  254        * if it cannot find a JDOClass instance for the specified name. 
  255        * <p>
  256        * Whether this method reads XML metatdata or not is determined at
  257        * JDOModel creation time (see flag <code>loadXMLMetadataDefault</code> 
  258        * in {@link JDOModelFactory#getJDOModel(JavaModel javaModel, boolean 
  259        * loadXMLMetadataDefault)}). Invoking this method is method is equivalent
  260        * to <code>createJDOClass(className, loadXMLMetadataDefault)</code>.
  261        * @param className the fully qualified class name of the JDOClass
  262        * instance to be returned
  263        * @return a JDOClass instance for the specified class name 
  264        * or <code>null</code> if not present
  265        */
  266       public JDOClass getJDOClass(String className) {
  267           return getJDOClass(className, loadXMLMetadataDefault);
  268       }
  269       
  270       /**
  271        * The method returns the JDOClass instance for the specified fully 
  272        * qualified class name if present. If the flag loadXMLMetadata is set 
  273        * to <code>true</code> the method tries to find the JDOClass instance by 
  274        * reading the XML metadata. The method returns null if it cannot find a 
  275        * JDOClass instance for the specified name.
  276        * @param className the fully qualified class name of the JDOClass instance 
  277        * to be returned
  278        * @param loadXMLMetadata indicate whether to read XML metatdata or not
  279        * @return a JDOClass instance for the specified class name
  280        * or <code>null</code> if not present
  281        * @exception ModelException if impossible
  282        */
  283       public synchronized JDOClass getJDOClass(String className, 
  284                                                boolean loadXMLMetadata) {
  285   
  286           boolean trace = logger.isTraceEnabled();
  287   
  288           // check whether the class is known to be non PC
  289           if (isKnownNonPC(className)) {
  290               if (trace)
  291                   logger.trace("JDOModel.getJDOClass: " + className + //NOI18N
  292                                " known to be non-persistence-capable"); //NOI18N
  293               return null;
  294           }
  295   
  296           JDOClass jdoClass = (JDOClass)jdoClasses.get(className);
  297           if (trace)
  298               logger.trace("JDOModel.getJDOClass: " + className + //NOI18N
  299                            ((jdoClass != null) ? " cached" : " not cached")); //NOI18N
  300   
  301           // check for XML metatdata
  302           if (loadXMLMetadata) {
  303               if (jdoClass == null)
  304                   jdoClass = lookupXMLMetadata(className);
  305               else if (!jdoClass.isXMLMetadataLoaded())
  306                   jdoClass = lookupXMLMetadata(jdoClass);
  307   
  308               if (jdoClass == null) {
  309                   // we loaded XML metadata, but there is no metadata
  310                   // for this class => known to be non persistence-capable
  311                   knownNonPC(className);
  312               }
  313           }
  314   
  315           return jdoClass;
  316       }
  317   
  318       /**
  319        * The method returns the JDOClass instance for the specified short name
  320        * (see {@link JDOClass#getShortName()}) or <code>null</code> if it cannot
  321        * find a JDOClass instance with the specified short name. 
  322        * <p>
  323        * The method searches the list of JDOClasses currently managed by this
  324        * JDOModel instance. It does not attempt to load any metadata if it
  325        * cannot find a JDOClass instance with the specified short name. The
  326        * metadata for a JDOClass returned by this method must have been loaded
  327        * before by any of the methods
  328        * {@link #createJDOClass(String className)},
  329        * {@link #createJDOClass(String className, boolean loadXMLMetadataDefault)},
  330        * {@link #getJDOClass(String className)}, or
  331        * {@link #getJDOClass(String className, boolean loadXMLMetadataDefault)}.
  332        * @param shortName the short name of the JDOClass instance to be returned
  333        * @return a JDOClass instance for the specified short name 
  334        * or <code>null</code> if not present
  335        */
  336       public synchronized JDOClass getJDOClassForShortName(String shortName) {
  337           if (StringHelper.isEmpty(shortName))
  338               return null;
  339   
  340           for (Iterator i = jdoClasses.values().iterator(); i.hasNext();) {
  341               JDOClass jdoClass = (JDOClass)i.next();
  342               if (shortName.equals(jdoClass.getShortName()))
  343                   // found => return
  344                   return jdoClass;
  345           }
  346   
  347           return null;
  348       }
  349   
  350       /**
  351        * Returns the collection of JDOClass instances declared by this JDOModel 
  352        * in the format of an array.
  353        * @return the classes declared by this JDOModel
  354        */
  355       public synchronized JDOClass[] getDeclaredClasses()  {
  356           return (JDOClass[])jdoClasses.values().toArray(
  357               new JDOClass[jdoClasses.size()]);
  358       }
  359   
  360       /**
  361        * Returns the JavaModel bound to this JDOModel instance.
  362        * @return the JavaModel
  363        */
  364       public JavaModel getJavaModel() {
  365           return javaModel;
  366       }
  367       
  368       /**
  369        * Sets the JavaModel for this JDOModel instance.
  370        * @param javaModel the JavaModel
  371        */
  372       public void setJavaModel(JavaModel javaModel) {
  373           this.javaModel = javaModel;
  374       }
  375       
  376       /**
  377        * Returns the parent JDOModel instance of this JDOModel.
  378        * @return the parent JDOModel
  379        */
  380       public JDOModel getParent() {
  381           if (javaModel != null) {
  382               JavaModel parentJavaModel = javaModel.getParent();
  383               if (parentJavaModel != null)
  384                   return parentJavaModel.getJDOModel();
  385           }
  386           return null; 
  387       }
  388   
  389       /**
  390        * This method returns the JDOClass instance that defines the specified type
  391        * as its objectId class. In the case of an inheritance hierarchy it returns 
  392        * the top most persistence-capable class of the hierarchy (see 
  393        * {@link JDOClass#getPersistenceCapableSuperclass}).
  394        * @param objectIdClass the type representation of the ObjectId class
  395        * @return the JDOClass defining the specified class as ObjectId class
  396        */
  397       public JDOClass getJDOClassForObjectIdClass(JavaType objectIdClass) {
  398           // Note, method getJDOClassForObjectIdClass is not synchronized to
  399           // avoid a deadlock with PC class registration.
  400           if (logger.isTraceEnabled())
  401               logger.trace("JDOModel.getJDOClassForObjectIdClass: " + //NOI18N
  402                            "check objectIdClass " +objectIdClass); //NOI18N
  403                           
  404           if (objectIdClass == null)
  405               return null;
  406   
  407           JDOClass jdoClass = null;
  408           String objectIdClassName = objectIdClass.getName();
  409           // check all JDOClasses for this JDOModel instance
  410           List classesToActivate = new ArrayList();
  411           while (true) {
  412               try {
  413                   for (Iterator i = jdoClasses.values().iterator(); i.hasNext();) {
  414                       JDOClass next = (JDOClass)i.next();
  415                       if (next.isXMLMetadataLoaded()) {
  416                           // XML metadata is loaded => check the objectIdClass
  417                           if (objectIdClassName.equals(
  418                                   next.getDeclaredObjectIdClassName())) {
  419                               // found => return
  420                               return next;
  421                           }
  422                       }
  423                       else {
  424                           // XML metadata is NOT loaded => 
  425                           // store the class for later processing.
  426                           // Do not load the XML metadata here. This might create
  427                           // new JDOClass instances in this model for other pc
  428                           // classes listed in the same .jdo file. This would
  429                           // change the jdoClasses map while its values are
  430                           // iterated => ConcurrentModificationException
  431                           classesToActivate.add(next);
  432                       }
  433                   }
  434                   // No ConcurrentModificationException => break the loop 
  435                   break;
  436               }
  437               catch (ConcurrentModificationException ex) {
  438                   // ConcurrentModificationException means a JDOClass was
  439                   // added to the JDOModel in parallel => 
  440                   // start the loop again.
  441               }
  442           }
  443                   
  444           // None of the activated JDOClasses knows the objectIdClass =>
  445           // activate the classes that were registered but not activated 
  446           // and check these classes
  447           for (Iterator i = classesToActivate.iterator(); i.hasNext();) {
  448               JDOClass next = (JDOClass)i.next();
  449               lookupXMLMetadata(next);
  450   
  451               if (objectIdClass.equals(next.getDeclaredObjectIdClassName())) {
  452                   // found => return
  453                   return next;
  454               }
  455           }
  456   
  457           // objectIdClass not found in this model => return null
  458           return null;
  459       }
  460   
  461       //========= Internal helper methods ==========
  462       
  463       /** Returns a new instance of the JDOClass implementation class. */
  464       protected JDOClass newJDOClassInstance(String name) {
  465           return new JDOClassImplDynamic(name);
  466       }
  467   
  468       /**
  469        * Checks whether the type with the specified name does NOT denote a
  470        * persistence-capable class.
  471        * @param typeName name of the type to be checked
  472        * @return <code>true</code> if types is a name of a primitive type; 
  473        * <code>false</code> otherwise
  474        */
  475       protected boolean isKnownNonPC(String typeName) {
  476           // Any class from packages java and javax are supposed to be non-pc.
  477           return typeName.startsWith("java.") || //NOI18N
  478                  typeName.startsWith("javax."); //NOI18N
  479       }
  480   
  481       /** 
  482        * Hook called when a class is known to be non persistence
  483        * capable. Subclasses might want to keep track of classes marked
  484        * as non pc classes and use this in the implementation of
  485        * {link #isKnownNonPC(String typeName)}.
  486        * @param className the name of the non-pc class
  487        */
  488       protected void knownNonPC(String className) {
  489       }
  490   
  491       /**
  492        * The method seaches JDO metadata for the class with the specified
  493        * class name. Chapter 18 of the JDO specification defines the search
  494        * order. The method skips resources that have been tried to load
  495        * before, no matter whether the resource currently exist. 
  496        * The method returns the populated JDOClass instance, if there is XML
  497        * metadata available for the specified class name. Otherwise it
  498        * returns <code>null</code>. 
  499        * <p>
  500        * The purpose of this method is to populate an existing JDOClass
  501        * instance with the JDO metadata. It throws an exception if there is
  502        * no jdo file for this class. The specified jdoClass must not be
  503        * <code>null</code>; otherwise a NullPointerException is thrown.
  504        * @param jdoClass the non-activated JDOClass instance.
  505        * @return the JDOClass instance for the specified class name or
  506        * <code>null</code> if there is no XML metadata.
  507        * @exception ModelFatalException indicates a problem while parsing the
  508        * XML file or a missing XML file.
  509        * @exception NullPointerException the specified jdoClass is
  510        * <code>null</code>.
  511        */
  512       private JDOClass lookupXMLMetadata(JDOClass jdoClass)
  513           throws ModelFatalException, NullPointerException {
  514           String className = jdoClass.getName();
  515           JDOClass activated = lookupXMLMetadata(className);
  516           if (activated == null) {
  517               throw new ModelFatalException(msg.msg(
  518                   "EXC_MissingJDOMetadata", className)); //NOI18N
  519           }
  520           else if (activated != jdoClass) {
  521               throw new ModelFatalException(msg.msg(
  522                   "ERR_MultipleJDOClassInstances", className)); //NOI18N
  523           }
  524           return jdoClass;
  525       }
  526   
  527       /**
  528        * The method seaches JDO metadata for the class with the specified
  529        * class name. Chapter 18 of the JDO specification defines the search
  530        * order. The method skips resources that have been tried to load
  531        * before, no matter whether the resource currently exist. 
  532        * The method returns the populated JDOClass instance, if there is XML
  533        * metadata available for the specified class name. Otherwise it
  534        * returns <code>null</code>. 
  535        * @param className the name of the class to check for XML metadata.
  536        * @return the JDOClass instance for the specified class name or
  537        * <code>null</code> if there is no XML metadata.
  538        */
  539       private JDOClass lookupXMLMetadata(String className) {
  540           boolean debug = xmlLogger.isDebugEnabled();
  541           JDOClass jdoClass = null;
  542           
  543           if (debug)
  544               xmlLogger.debug("JDOModel.lookupXMLMetadata:" + // NOI18N
  545                               " lookup XML for class " + className); // NOI18N
  546           // Iterate possible resources to find JDO metadata for className
  547           Iterator i = new MetadataResourceNameIterator(className);
  548           while((jdoClass == null) && i.hasNext()) {
  549               String resource = (String)i.next();
  550               if (processedResources.contains(resource)) {
  551                   if (debug)
  552                       xmlLogger.debug("  XML " + resource + //NOI18N
  553                                       " processed already"); //NOI18N
  554                   // processed already => skip
  555                   continue;
  556               }
  557               else {
  558                   // add resource to the list of processed resources
  559                   // Note, this adds the resource no matter whether the
  560                   // resource existst or not. This implies this JDOModel 
  561                   // instance will not check this resource again, 
  562                   // even if it exists, again.
  563                   processedResources.add(resource);
  564                   // load resource
  565                   jdoClass = loadXMLResource(resource, className);
  566               }
  567           }
  568           if (debug)
  569               xmlLogger.debug("JDOModel.lookupXMLMetadata: " +  //NOI18N
  570                               ((jdoClass!=null)?"found":"no") + //NOI18N
  571                               " JDO metadata for class " + className); //NOI18N
  572           return jdoClass;
  573       }
  574   
  575       /**
  576        * Load the specified resource assuming it contains JDO metadata for
  577        * one or more persistence capable classes. 
  578        * The method returns the populated JDOClass instance, if the specified
  579        * resource has XML metadata for the specified class name. Otherwise it
  580        * returns <code>null</code>. 
  581        * @param resource resource to load
  582        * @param className the name of the class to check for XML metadata.
  583        * @return the JDOClass instance for the specified class name or
  584        * <code>null</code> if the specified resource has no XML metadata.
  585        */
  586       private JDOClass loadXMLResource(String resource, String className) {
  587           boolean debug = xmlLogger.isDebugEnabled();
  588           JDOClass jdoClass = null;
  589           InputStream stream = javaModel.getInputStreamForResource(resource);
  590           if (stream == null) {
  591               if (debug)
  592                   xmlLogger.debug("  XML " + resource + " not available"); //NOI18N
  593           }
  594           else {
  595               // resource exists => parse it
  596               // Store all pc classes specified in this resource in this
  597               // JDOModel instance => pass this to the handler
  598               JDOHandler handler = new JDOHandlerImpl(this);
  599               JDOParser parser = new JDOParser(handler);
  600               try {
  601                   if (debug)
  602                       xmlLogger.debug("  XML " + resource +  //NOI18N
  603                                       " found, start parsing ..."); //NOI18N
  604                   parser.parse(new InputSource(new InputStreamReader(stream)));
  605               }
  606               catch (SAXException ex) {
  607                   throw new ModelFatalException(
  608                       msg.msg("EXC_XMLError", resource), ex); //NOI18N
  609               }
  610               catch (ParserConfigurationException ex) {
  611                   throw new ModelFatalException(
  612                       msg.msg("EXC_XMLError", resource), ex); //NOI18N
  613               }
  614               catch (IOException ex) {
  615                   throw new ModelFatalException(
  616                       msg.msg("EXC_XMLError", resource), ex); //NOI18N
  617               }
  618               finally {
  619                   try { stream.close(); }
  620                   catch (IOException ex) { 
  621                       // ignore close exception, stream will be nullified anyway 
  622                   }
  623               }
  624               stream = null;
  625               
  626               // post process loaded JDOClasses
  627               Collection newJDOClasses = handler.handledJDOClasses();
  628               if (debug)
  629                   xmlLogger.debug("  XML " + resource + //NOI18N
  630                                   " has JDO metadata for class(es) " + //NOI18N
  631                                   newJDOClasses);
  632               for (Iterator i = newJDOClasses.iterator(); i.hasNext();) {
  633                   JDOClass next = (JDOClass)i.next();
  634                   if (className.equals(next.getName())) {
  635                       jdoClass = next;
  636                   }
  637                   checkSuperclass(next);
  638               }
  639               if (jdoClass == null)
  640                   if (debug)
  641                       xmlLogger.debug("  XML " + resource + //NOI18N
  642                                       " does not have JDO metadata for class " + //NOI18N
  643                                       className); 
  644           }
  645           return jdoClass;
  646       }
  647   
  648       /**
  649        * Updates the pcSuperclass property of the specified JDOClass and all its 
  650        * superclasses. 
  651        * @param jdoClass the class to be checked
  652        */
  653       private void checkSuperclass(JDOClass jdoClass) {
  654           if (jdoClass != null) {
  655               JDOClass superclass = jdoClass.getPersistenceCapableSuperclass();
  656               checkSuperclass(superclass);
  657           }
  658       }
  659   
  660       /** Returns the package name of a class name */
  661       private String getPackageName(String className) {
  662           int index = className.lastIndexOf('.');
  663           return (index == -1) ? "" : className.substring(0, index); //NOI18N
  664       }
  665       
  666       /**
  667        * Returns the UnresolvedRelationshipHelper instance for this JDOModel
  668        * instance. 
  669        * @return the current UnresolvedRelationshipHelper
  670        */
  671       UnresolvedRelationshipHelper getUnresolvedRelationshipHelper() {
  672           return unresolvedRelationshipHelper;
  673       }
  674   
  675       /**
  676        * This Iterator implementation iterates resource names of possible JDO
  677        * metadata files for the specified class name. Chapter 18 of the JDO
  678        * specification defines the search order as follows: 
  679        * META-INF/package.jdo, WEB-INF/package.jdo, package.jdo, 
  680        * <package>/.../<package>/package.jdo, and <package>/<class>.jdo.
  681        */
  682       private static class MetadataResourceNameIterator 
  683           implements Iterator {
  684           /** Suffix of a JDO metadata file. */
  685           private static final String JDO_SUFFIX = ".jdo"; //NOI18N
  686   
  687           /** The name of a package JDO metadata file. */
  688           private static final String PACKAGE_JDO = "package" + JDO_SUFFIX; //NOI18N
  689   
  690           /** List of constant package JDO metadata file names. */
  691           private static final String[] constantResources = { 
  692               "META-INF/" + PACKAGE_JDO, //NOI18N
  693               "WEB-INF/" + PACKAGE_JDO,  //NOI18N
  694               PACKAGE_JDO 
  695           };
  696   
  697           /** Indicates whether this iterator has more elements. */
  698           private boolean hasNext = true;
  699   
  700           /** The class name as resource name. */
  701           private final String prefix;
  702   
  703           /** Current index in the list of constant package JDO metadata file names. */
  704           private int constantResourcesIndex = 0;
  705   
  706           /** Current index in the prefix. */
  707           private int fromIndex = 0;
  708   
  709           /** Constructor. */
  710           public MetadataResourceNameIterator(String className) {
  711               this.prefix = className.replace('.', '/');
  712           }
  713           
  714           /**
  715            * Returns <code>true</code> if the iteration has more elements.
  716            * @return <code>true</code> if the iterator has more elements.
  717            */
  718           public boolean hasNext() {
  719               return hasNext;
  720           }
  721           
  722           /**
  723            * Returns the next resource name.
  724            * @return the next resource name.
  725            * @exception NoSuchElementException iteration has no more elements. 
  726            */
  727           public Object next() {
  728               String resource = null;
  729               if (!hasNext) {
  730                   throw new NoSuchElementException();
  731               }
  732               else if (constantResourcesIndex < constantResources.length) {
  733                   // Use a constant resource name, if there is one left in
  734                   // the iteration.
  735                   resource = constantResources[constantResourcesIndex];
  736                   constantResourcesIndex++;
  737               }
  738               else {
  739                   // No constant resource name left
  740                   // build resource names from the package of the class name
  741                   // Check for the next package, fromIndex is the index of
  742                   // the '/' used in the previous iteration.
  743                   int index = prefix.indexOf('/', fromIndex);
  744                   if (index != -1) {
  745                       // string needs to include '/' => use index+1
  746                       resource = prefix.substring(0, index + 1) + PACKAGE_JDO;
  747                       fromIndex = index + 1;
  748                   }
  749                   else {
  750                       // no more package jdo files left => use class .jdo file 
  751                       resource = prefix + JDO_SUFFIX;
  752                       // the class jdo file is the last resource to be checked 
  753                       hasNext = false;
  754                   }
  755               }
  756               return resource;
  757           }
  758   
  759           /**
  760            * This Iterator does not implement this method.
  761            * @exception UnsupportedOperationException if the
  762            * <code>remove</code> operation is not supported by this
  763            * Iterator. 
  764            */
  765           public void remove() {
  766               throw new UnsupportedOperationException();
  767           }
  768           
  769       }
  770   
  771   }

Save This Page
Home » jdo2-model-2.3-ea-src » org.apache.jdo.impl.model.jdo » [javadoc | source]