1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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");
133
134 /*** Logger */
135 protected static Log logger =
136 LogFactory.getFactory().getInstance("org.apache.jdo.impl.model.jdo");
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);
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)/package-summary.html">ong> JDOPackage createJDOPackage(String packageName)
166 throws ModelException {
167 JDOPackage jdoPackage = getJDOPackage(packageName)/package-summary.html">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) {/package-summary.html">ong> JDOPackage getJDOPackage(String packageName) {
187 return</strong> (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: " +
241 "create new JDOClass instance " + className);
242 jdoClass = newJDOClassInstance(className);
243 jdoClass.setDeclaringModel(this);
244 jdoClasses.put(className, jdoClass);
245
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
289 if (isKnownNonPC(className)) {
290 if (trace)
291 logger.trace("JDOModel.getJDOClass: " + className +
292 " known to be non-persistence-capable");
293 return null;
294 }
295
296 JDOClass jdoClass = (JDOClass)jdoClasses.get(className);
297 if (trace)
298 logger.trace("JDOModel.getJDOClass: " + className +
299 ((jdoClass != null) ? " cached" : " not cached"));
300
301
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
310
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
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
399
400 if (logger.isTraceEnabled())
401 logger.trace("JDOModel.getJDOClassForObjectIdClass: " +
402 "check objectIdClass " +objectIdClass);
403
404 if (objectIdClass == null)
405 return null;
406
407 JDOClass jdoClass = null;
408 String objectIdClassName = objectIdClass.getName();
409
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
417 if (objectIdClassName.equals(
418 next.getDeclaredObjectIdClassName())) {
419
420 return next;
421 }
422 }
423 else {
424
425
426
427
428
429
430
431 classesToActivate.add(next);
432 }
433 }
434
435 break;
436 }
437 catch (ConcurrentModificationException ex) {
438
439
440
441 }
442 }
443
444
445
446
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
453 return next;
454 }
455 }
456
457
458 return null;
459 }
460
461
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
477 return typeName.startsWith("java.") ||
478 typeName.startsWith("javax.");
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));
519 }
520 else if (activated != jdoClass) {
521 throw new ModelFatalException(msg.msg(
522 "ERR_MultipleJDOClassInstances", className));
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:" +
545 " lookup XML for class " + className);
546
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 +
553 " processed already");
554
555 continue;
556 }
557 else {
558
559
560
561
562
563 processedResources.add(resource);
564
565 jdoClass = loadXMLResource(resource, className);
566 }
567 }
568 if (debug)
569 xmlLogger.debug("JDOModel.lookupXMLMetadata: " +
570 ((jdoClass!=null)?"found":"no") +
571 " JDO metadata for class " + className);
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");
593 }
594 else {
595
596
597
598 JDOHandler handler = new JDOHandlerImpl(this);
599 JDOParser parser = new JDOParser(handler);
600 try {
601 if (debug)
602 xmlLogger.debug(" XML " + resource +
603 " found, start parsing ...");
604 parser.parse(new InputSource(new InputStreamReader(stream)));
605 }
606 catch (SAXException ex) {
607 throw new ModelFatalException(
608 msg.msg("EXC_XMLError", resource), ex);
609 }
610 catch (ParserConfigurationException ex) {
611 throw new ModelFatalException(
612 msg.msg("EXC_XMLError", resource), ex);
613 }
614 catch (IOException ex) {
615 throw new ModelFatalException(
616 msg.msg("EXC_XMLError", resource), ex);
617 }
618 finally {
619 try { stream.close(); }
620 catch (IOException ex) {
621
622 }
623 }
624 stream = null;
625
626
627 Collection newJDOClasses = handler.handledJDOClasses();
628 if (debug)
629 xmlLogger.debug(" XML " + resource +
630 " has JDO metadata for class(es) " +
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 +
642 " does not have JDO metadata for class " +
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 /**</package-summary/html">Returns the package name of a class name *//package-summary.html">em>* 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);
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";
686
687 /package-summary/html">The name of a package JDO metadata file/ *//package-summary.html">/*** The name of a package JDO metadata file. */
688 private static final String PACKAGE_JDO = "package" + JDO_SUFFIX;
689
690 /package-summary/html">List of constant package JDO metadata file names/ *//package-summary.html">/*** List of constant package JDO metadata file names. */
691 private static final String[] constantResources = {
692 "META-INF/" + PACKAGE_JDO,
693 "WEB-INF/" + PACKAGE_JDO,
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 /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. */
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
734
735 resource = constantResources[constantResourcesIndex];
736 constantResourcesIndex++;
737 }
738 else {
739
740
741
742
743 int index = prefix.indexOf('/', fromIndex);
744 if (index != -1) {
745
746 resource = prefix.substring(0, index + 1) + PACKAGE_JDO;
747 fromIndex = index + 1;
748 }
749 else {
750
751 resource = prefix + JDO_SUFFIX;
752
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 }