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