1 10 11 package com.triactive.jdo.model; 12 13 import com.triactive.jdo.ClassNotPersistenceCapableException; 14 import com.triactive.jdo.util.MacroString; 15 import com.triactive.jdo.util.XMLHelper; 16 import java.lang.reflect.Field ; 17 import java.lang.reflect.Modifier ; 18 import java.net.URL ; 19 import java.util.Arrays ; 20 import java.util.ArrayList ; 21 import java.util.Collections ; 22 import java.util.HashMap ; 23 import java.util.HashSet ; 24 import java.util.Iterator ; 25 import java.util.List ; 26 import java.util.Map ; 27 import java.util.Set ; 28 import java.util.WeakHashMap ; 29 import javax.jdo.JDOFatalInternalException; 30 import javax.jdo.JDOFatalUserException; 31 import javax.jdo.JDOUserException; 32 import javax.jdo.spi.JDOImplHelper; 33 import javax.jdo.spi.PersistenceCapable; 34 import org.w3c.dom.Element ; 35 import org.w3c.dom.Node ; 36 import org.w3c.dom.NodeList ; 37 import org.apache.log4j.Category; 38 39 40 public final class ClassMetaData extends MetaData 41 { 42 private static final Category LOG = Category.getInstance(ClassMetaData.class); 43 44 private static Map metaDataByClass = new WeakHashMap (); 45 private static Map searchedPathsByClassLoader = new WeakHashMap (); 46 47 public static synchronized ClassMetaData forClass(Class c) 48 { 49 if (c.isArray() || c.isInterface() || c.isPrimitive()) 50 return null; 51 52 if (metaDataByClass.containsKey(c)) 53 return (ClassMetaData)metaDataByClass.get(c); 54 55 ClassLoader cl = c.getClassLoader(); 56 57 if (cl == null) 58 cl = ClassLoader.getSystemClassLoader(); 59 60 Set searchedPaths = (Set )searchedPathsByClassLoader.get(cl); 61 62 if (searchedPaths == null) 63 { 64 searchedPaths = new HashSet (); 65 searchedPathsByClassLoader.put(cl, searchedPaths); 66 } 67 68 ClassMetaData cmd = null; 69 Iterator i = possiblePathsFor(c).iterator(); 70 71 while (cmd == null && i.hasNext()) 72 { 73 String path = (String )i.next(); 74 75 if (!searchedPaths.contains(path)) 76 { 77 if (LOG.isDebugEnabled()) 78 LOG.debug("Searching for metadata at " + path + " using " + cl); 79 80 URL url = cl.getResource(path); 81 82 if (url != null) 83 cmd = loadMetadataFileLookingFor(url, c, cl); 84 85 searchedPaths.add(path); 86 } 87 } 88 89 if (cmd != null) 90 cmd.assertIsValidated(); 91 92 return cmd; 93 } 94 95 private static List possiblePathsFor(Class c) 96 { 97 ArrayList paths = new ArrayList (); 98 99 paths.add("META-INF/package.jdo"); 100 paths.add("WEB-INF/package.jdo"); 101 paths.add("package.jdo"); 102 103 String path = c.getName().replace('.', '/'); 104 105 for (int slash = 0; (slash = path.indexOf('/', slash)) >= 0;) 106 paths.add(path.substring(0, ++slash) + "package.jdo"); 107 108 paths.add(path + ".jdo"); 109 110 111 for (int slash = path.length() - 1; (slash = path.lastIndexOf('/', slash)) >= 0;) 112 paths.add(path.substring(0, slash--) + ".jdo"); 113 114 return paths; 115 } 116 117 private static ClassMetaData loadMetadataFileLookingFor(URL url, Class targetClass, ClassLoader cl) 118 { 119 if (LOG.isDebugEnabled()) 120 LOG.debug("Loading metadata from " + url); 121 122 ClassMetaData found = null; 123 Element docElement; 124 125 try 126 { 127 docElement = XMLHelper.getDocumentBuilder().parse(url.openStream()).getDocumentElement(); 128 } 129 catch (Exception e) 130 { 131 throw new XMLMetaDataException(url, "Could not access or parse metadata file", e); 132 } 133 134 NodeList nodes = docElement.getElementsByTagName("class"); 135 136 for (int i = 0; i < nodes.getLength(); ++i) 137 { 138 Element clsElement = (Element )nodes.item(i); 139 String packageName = ((Element )clsElement.getParentNode()).getAttribute("name"); 140 String className = packageName + '.' + clsElement.getAttribute("name"); 141 Class c; 142 143 try 144 { 145 151 c = Class.forName(className, true, cl); 152 } 153 catch (ClassNotFoundException e) 154 { 155 throw new XMLMetaDataException(url, "Class" + className + " not found", e); 156 } 157 158 if (!metaDataByClass.containsKey(c)) 159 { 160 ClassMetaData cmd = new ClassMetaData(c, url, clsElement); 161 metaDataByClass.put(c, cmd); 162 163 if (c.equals(targetClass)) 164 found = cmd; 165 } 166 } 167 168 return found; 169 } 170 171 172 public static final byte 173 NEW = 0, 174 CONSTRUCTED = 1, 175 VALIDATED = 2; 176 177 public static final int 178 NO_IDENTITY = 0, 179 DATASTORE_IDENTITY = 1, 180 APPLICATION_IDENTITY = 2; 181 182 private static final List identityTypeValues = Arrays.asList(new String [] 183 {"nondurable", "datastore", "application"}); 184 185 186 private final Class clazz; 187 private final ClassLoader loader; 188 private final String packageName; 189 private final URL loadedFrom; 190 private final int identityType; 191 private final Class identityClass; 192 private final boolean requiresExtent; 193 private final Class pcSuperclass; 194 private final List managedFields = new ArrayList (); 195 private final Map fieldNumbersByName = new HashMap (); 196 197 private byte state = NEW; 198 private boolean validating = false; 199 200 201 private ClassMetaData pcSuperclassMetaData; 202 private int inheritedFieldCount; 203 private int totalFieldCount; 204 private int[] allFieldNumbers; 205 private int[] transactionalFieldNumbers; 206 private int[] persistentFieldNumbers; 207 private int[] defaultFetchGroupFieldNumbers; 208 private int[] secondClassMutableFieldNumbers; 209 private boolean[] transactionalFieldFlags; 210 private boolean[] persistentFieldFlags; 211 private boolean[] defaultFetchGroupFieldFlags; 212 private boolean[] secondClassMutableFieldFlags; 213 214 215 private ClassMetaData(Class clazz, URL loadedFrom, Element clsElement) 216 { 217 super(loadedFrom, clsElement); 218 219 String className = clazz.getName(); 220 int lastDot = className.lastIndexOf('.'); 221 222 this.clazz = clazz; 223 this.loader = clazz.getClassLoader(); 224 this.packageName = lastDot < 0 ? "" : className.substring(0, lastDot); 225 this.loadedFrom = loadedFrom; 226 227 228 String idTypeAttr = clsElement.getAttribute("identity-type"); 229 230 if (idTypeAttr.length() > 0) 231 { 232 identityType = identityTypeValues.indexOf(idTypeAttr); 233 234 if (identityType < 0) 235 throw new XMLMetaDataException(loadedFrom, "Unrecognized identity-type " + idTypeAttr); 236 } 237 else 238 identityType = DATASTORE_IDENTITY; 239 240 if (identityType == APPLICATION_IDENTITY) 241 { 242 243 String objIDClassName = clsElement.getAttribute("objectid-class"); 244 if (objIDClassName.length() == 0) 245 throw new XMLMetaDataException(loadedFrom, "Missing objectid-class attribute for class " + clazz.getName()); 246 247 if (objIDClassName.indexOf('.') < 0) 248 objIDClassName = packageName + '.' + objIDClassName; 249 250 try 251 { 252 identityClass = Class.forName(objIDClassName, true, loader); 253 } 254 catch (ClassNotFoundException e) 255 { 256 throw new XMLMetaDataException(loadedFrom, "Object ID class for class" + clazz.getName() + " not found", e); 257 } 258 } 259 else 260 identityClass = null; 261 262 263 String requiresExtentAttr = clsElement.getAttribute("requires-extent"); 264 if (requiresExtentAttr.length() > 0) 265 requiresExtent = Boolean.valueOf(requiresExtentAttr).booleanValue(); 266 else 267 requiresExtent = true; 268 269 270 String pcSuperclassName = clsElement.getAttribute("persistence-capable-superclass"); 271 if (pcSuperclassName.length() > 0) 272 { 273 Class c; 274 275 try 276 { 277 c = Class.forName(pcSuperclassName, true, loader); 278 } 279 catch (ClassNotFoundException e) 280 { 281 try 282 { 283 c = Class.forName(packageName + '.' + pcSuperclassName, true, loader); 284 } 285 catch (ClassNotFoundException e1) 286 { 287 throw new XMLMetaDataException(loadedFrom, "Class not found", e1); 288 } 289 } 290 291 pcSuperclass = c; 292 293 if (pcSuperclass.equals(clazz) || !pcSuperclass.isAssignableFrom(clazz)) 294 throw new XMLMetaDataException(loadedFrom, "Specified persistence capable superclass " + pcSuperclass.getName() + " is not a superclass of " + clazz.getName()); 295 } 296 else 297 pcSuperclass = null; 298 299 300 for (Node node = clsElement.getFirstChild(); node != null; node = node.getNextSibling()) 301 { 302 if (node instanceof Element ) 303 { 304 Element child = (Element )node; 305 String childTag = child.getTagName(); 306 307 if (childTag.equals("field")) 308 addField(new FieldMetaData(this, child)); 309 } 310 } 311 312 316 Field declaredFields[] = clazz.getDeclaredFields(); 317 318 for (int i = 0; i < declaredFields.length; ++i) 319 { 320 Field f = declaredFields[i]; 321 322 if (!fieldNumbersByName.containsKey(f.getName())) 323 { 324 int m = f.getModifiers(); 325 326 if (!Modifier.isFinal(m) && 327 !Modifier.isStatic(m) && 328 !Modifier.isTransient(m) && 329 Types.isDefaultPersistentType(f.getType())) 330 { 331 addField(new FieldMetaData(this, f)); 332 } 333 } 334 } 335 336 340 Collections.sort(managedFields); 341 Iterator iter = managedFields.iterator(); 342 343 for (int i = 0; iter.hasNext(); ++i) 344 { 345 FieldMetaData fmd = (FieldMetaData)iter.next(); 346 fieldNumbersByName.put(fmd.getName(), new Integer (i)); 347 } 348 349 state = CONSTRUCTED; 350 } 351 352 353 private void addField(FieldMetaData fm) 354 { 355 String name = fm.getName(); 356 if (fieldNumbersByName.containsKey(name)) 357 throw new DuplicateFieldException(loadedFrom, "Duplicate field name " + name + " in class " + clazz.getName()); 358 359 fieldNumbersByName.put(name, null); 360 361 if (fm.getPersistenceModifier() != FieldMetaData.PERSISTENCE_MODIFIER_NONE) 362 managedFields.add(fm); 363 } 364 365 private synchronized void assertIsConstructed() 366 { 367 if (state < CONSTRUCTED) 368 throw new JDOFatalInternalException("Object not yet constructed: " + this); 369 } 370 371 public String getJavaName() 372 { 373 return clazz.getName(); 374 } 375 376 public Class getPCClass() 377 { 378 return clazz; 379 } 380 381 public String getPackageName() 382 { 383 return packageName; 384 } 385 386 public URL getSourceURL() 387 { 388 return loadedFrom; 389 } 390 391 public Class getPCSuperclass() 392 { 393 return pcSuperclass; 394 } 395 396 public int getIdentityType() 397 { 398 return identityType; 399 } 400 401 public Class getIdentityClass() 402 { 403 return identityClass; 404 } 405 406 public boolean requiresExtent() 407 { 408 return requiresExtent; 409 } 410 411 public int getFieldCount() 412 { 413 assertIsConstructed(); 414 return managedFields.size(); 415 } 416 417 public FieldMetaData getFieldRelative(int relativeFieldNumber) 418 { 419 assertIsConstructed(); 420 return (FieldMetaData)managedFields.get(relativeFieldNumber); 421 } 422 423 public int getRelativeFieldNumber(String name) 424 { 425 assertIsConstructed(); 426 Integer i = (Integer )fieldNumbersByName.get(name); 427 428 return i == null ? -1 : i.intValue(); 429 } 430 431 public String getViewImports() 432 { 433 return getVendorExtension(MY_VENDOR, "view-imports"); 434 } 435 436 public String getViewDefinition(String vendorID) 437 { 438 String viewDef = null; 439 440 if (vendorID != null) 441 viewDef = getVendorExtension(MY_VENDOR, "view-definition" + '-' + vendorID); 442 443 if (viewDef == null) 444 viewDef = getVendorExtension(MY_VENDOR, "view-definition"); 445 446 return viewDef; 447 } 448 449 private synchronized boolean assertIsValidated() 450 { 451 assertIsConstructed(); 452 453 if (state < VALIDATED) 454 { 455 if (validating) 456 throw new JDOFatalInternalException("assertIsValidated() called recursively"); 457 458 validating = true; 459 460 try 461 { 462 validateAgainstClass(); 463 } 464 finally 465 { 466 validating = false; 467 } 468 469 return true; 470 } 471 else 472 return false; 473 } 474 475 private void validateAgainstClass() 476 { 477 481 if (Types.isEnhancedClass(clazz)) 482 { 483 JDOImplHelper helper = JDOImplHelper.getInstance(); 484 String [] fieldNames = helper.getFieldNames(clazz); 485 Class [] fieldTypes = helper.getFieldTypes(clazz); 486 byte[] fieldFlags = helper.getFieldFlags(clazz); 487 488 if (fieldNames.length != managedFields.size()) 489 throw new ClassMetaDataMismatchException(clazz, " class has " + fieldNames.length + " fields, metadata has " + managedFields.size() + " fields"); 490 491 for (int i = 0; i < fieldNames.length; ++i) 492 { 493 FieldMetaData fmd = (FieldMetaData)managedFields.get(i); 494 495 if (!fieldNames[i].equals(fmd.getName())) 496 throw new ClassMetaDataMismatchException(clazz, " class has '" + fieldNames[i] + "' for field " + i + ", metadata has '" + fmd.getName() + "'"); 497 498 if (!fieldTypes[i].equals(fmd.getType())) 499 throw new ClassMetaDataMismatchException(clazz, " class has type " + fieldTypes[i].getName() + " for field " + i + ", metadata has " + fmd.getType().getName()); 500 501 511 byte expectedFlag; 512 byte actualFlag = (byte)(fieldFlags[i] & (PersistenceCapable.CHECK_READ | 513 PersistenceCapable.CHECK_WRITE | 514 PersistenceCapable.MEDIATE_READ | 515 PersistenceCapable.MEDIATE_WRITE)); 516 517 if (fmd.getPersistenceModifier() == FieldMetaData.PERSISTENCE_MODIFIER_TRANSACTIONAL) 518 { 519 expectedFlag = PersistenceCapable.CHECK_WRITE; 520 } 521 else if (fmd.isPrimaryKeyPart()) 522 { 523 expectedFlag = PersistenceCapable.MEDIATE_WRITE; 524 } 525 else if (fmd.isInDefaultFetchGroup()) 526 { 527 expectedFlag = PersistenceCapable.CHECK_READ | PersistenceCapable.CHECK_WRITE; 528 529 533 if (actualFlag == (PersistenceCapable.MEDIATE_READ | PersistenceCapable.MEDIATE_WRITE)) 534 { 535 LOG.warn(new ClassMetaDataFlagMismatchException(clazz, i, expectedFlag, actualFlag).getMessage() + ": will use the value in class (non-default fetch group)"); 536 537 expectedFlag = PersistenceCapable.MEDIATE_READ | PersistenceCapable.MEDIATE_WRITE; 538 fmd.setDefaultFetchGroup(false); 539 } 540 } 541 else 542 { 543 expectedFlag = PersistenceCapable.MEDIATE_READ | PersistenceCapable.MEDIATE_WRITE; 544 545 549 if (actualFlag == (PersistenceCapable.CHECK_READ | PersistenceCapable.CHECK_WRITE)) 550 { 551 LOG.warn(new ClassMetaDataFlagMismatchException(clazz, i, expectedFlag, actualFlag).getMessage() + ": will use the value in class (default fetch group)"); 552 553 expectedFlag = PersistenceCapable.CHECK_READ | PersistenceCapable.CHECK_WRITE; 554 fmd.setDefaultFetchGroup(true); 555 } 556 } 557 558 if (expectedFlag != actualFlag) 559 throw new ClassMetaDataFlagMismatchException(clazz, i, expectedFlag, actualFlag); 560 } 561 } 562 563 if (pcSuperclass != null) 564 { 565 pcSuperclassMetaData = forClass(pcSuperclass); 566 567 if (pcSuperclassMetaData == null) 568 throw new XMLMetaDataException(loadedFrom, "Specified persistence capable superclass " + pcSuperclass.getName() + " is not persistence capable"); 569 570 inheritedFieldCount = pcSuperclassMetaData.getInheritedFieldCount() + pcSuperclassMetaData.getFieldCount(); 571 } 572 else 573 inheritedFieldCount = 0; 574 575 totalFieldCount = inheritedFieldCount + managedFields.size(); 576 allFieldNumbers = new int[totalFieldCount]; 577 transactionalFieldFlags = new boolean[totalFieldCount]; 578 persistentFieldFlags = new boolean[totalFieldCount]; 579 defaultFetchGroupFieldFlags = new boolean[totalFieldCount]; 580 secondClassMutableFieldFlags = new boolean[totalFieldCount]; 581 int transactionalFieldCount = 0; 582 int persistentFieldCount = 0; 583 int defaultFetchGroupFieldCount = 0; 584 int secondClassMutableFieldCount = 0; 585 586 for (int i = 0; i < totalFieldCount; ++i) 587 { 588 allFieldNumbers[i] = i; 589 590 FieldMetaData fmd = getFieldAbsoluteInternal(i); 591 592 if (fmd.getPersistenceModifier() == FieldMetaData.PERSISTENCE_MODIFIER_TRANSACTIONAL) 593 { 594 transactionalFieldFlags[i] = true; 595 ++transactionalFieldCount; 596 } 597 else 598 { 599 persistentFieldFlags[i] = true; 600 ++persistentFieldCount; 601 602 if (fmd.isInDefaultFetchGroup()) 603 { 604 defaultFetchGroupFieldFlags[i] = true; 605 ++defaultFetchGroupFieldCount; 606 } 607 608 if (Types.isSecondClassMutableType(fmd.getType())) 609 { 610 secondClassMutableFieldFlags[i] = true; 611 ++secondClassMutableFieldCount; 612 } 613 } 614 } 615 616 transactionalFieldNumbers = new int[transactionalFieldCount]; 617 persistentFieldNumbers = new int[persistentFieldCount]; 618 defaultFetchGroupFieldNumbers = new int[defaultFetchGroupFieldCount]; 619 secondClassMutableFieldNumbers = new int[secondClassMutableFieldCount]; 620 621 for (int i = 0, txn = 0, per = 0, dfg = 0, scm = 0; i < totalFieldCount; ++i) 622 { 623 if (transactionalFieldFlags[i]) 624 transactionalFieldNumbers[txn++] = i; 625 626 if (persistentFieldFlags[i]) 627 persistentFieldNumbers[per++] = i; 628 629 if (defaultFetchGroupFieldFlags[i]) 630 defaultFetchGroupFieldNumbers[dfg++] = i; 631 632 if (secondClassMutableFieldFlags[i]) 633 secondClassMutableFieldNumbers[scm++] = i; 634 } 635 636 state = VALIDATED; 637 } 638 639 private FieldMetaData getFieldAbsoluteInternal(int absoluteFieldNumber) 640 { 641 if (absoluteFieldNumber < inheritedFieldCount) 642 { 643 if (pcSuperclassMetaData == null) 644 return null; 645 else 646 return pcSuperclassMetaData.getFieldAbsoluteInternal(absoluteFieldNumber); 647 } 648 else 649 return (FieldMetaData)managedFields.get(absoluteFieldNumber - inheritedFieldCount); 650 } 651 652 public int getInheritedFieldCount() 653 { 654 assertIsValidated(); 655 return inheritedFieldCount; 656 } 657 658 public FieldMetaData getFieldAbsolute(int absoluteFieldNumber) 659 { 660 assertIsValidated(); 661 return getFieldAbsoluteInternal(absoluteFieldNumber); 662 } 663 664 public int getAbsoluteFieldNumber(String name) 665 { 666 assertIsValidated(); 667 int lastDot = name.lastIndexOf('.'); 668 669 if (lastDot >= 0) 670 { 671 672 Class c; 673 674 try 675 { 676 c = Class.forName(name.substring(0, lastDot), true, loader); 677 } 678 catch (ClassNotFoundException e) 679 { 680 throw new JDOUserException("Class in field name not found: " + name, e); 681 } 682 683 ClassMetaData cmd = ClassMetaData.forClass(c); 684 685 if (cmd == null) 686 throw new ClassNotPersistenceCapableException(c); 687 688 if (!c.isAssignableFrom(clazz)) 689 throw new JDOUserException("Class in field name " + name + " not compatible with actual class " + clazz.getName()); 690 691 return cmd.getAbsoluteFieldNumber(name.substring(lastDot + 1)); 692 } 693 else 694 { 695 696 int i = getRelativeFieldNumber(name); 697 698 if (i < 0) 699 { 700 if (pcSuperclassMetaData != null) 701 i = pcSuperclassMetaData.getAbsoluteFieldNumber(name); 702 } 703 else 704 i += inheritedFieldCount; 705 706 return i; 707 } 708 } 709 710 public int[] getAllFieldNumbers() 711 { 712 assertIsValidated(); 713 return allFieldNumbers; 714 } 715 716 public int[] getTransactionalFieldNumbers() 717 { 718 assertIsValidated(); 719 return transactionalFieldNumbers; 720 } 721 722 public int[] getPersistentFieldNumbers() 723 { 724 assertIsValidated(); 725 return persistentFieldNumbers; 726 } 727 728 public int[] getDefaultFetchGroupFieldNumbers() 729 { 730 assertIsValidated(); 731 return defaultFetchGroupFieldNumbers; 732 } 733 734 public int[] getSecondClassMutableFieldNumbers() 735 { 736 assertIsValidated(); 737 return secondClassMutableFieldNumbers; 738 } 739 740 public boolean[] getTransactionalFieldFlags() 741 { 742 assertIsValidated(); 743 return transactionalFieldFlags; 744 } 745 746 public boolean[] getPersistentFieldFlags() 747 { 748 assertIsValidated(); 749 return persistentFieldFlags; 750 } 751 752 public boolean[] getDefaultFetchGroupFieldFlags() 753 { 754 assertIsValidated(); 755 return defaultFetchGroupFieldFlags; 756 } 757 758 public boolean[] getSecondClassMutableFieldFlags() 759 { 760 assertIsValidated(); 761 return secondClassMutableFieldFlags; 762 } 763 764 public List getReferencedClasses(String vendorID) 765 { 766 Set referenced = new HashSet (); 767 List orderedMetaData = new ArrayList (); 768 769 getReferencedClasses(vendorID, orderedMetaData, referenced); 770 771 return orderedMetaData; 772 } 773 774 791 void getReferencedClasses(final String vendorID, final List orderedCmds, 792 final Set referenced) 793 { 794 assertIsValidated(); 795 Map viewReferences = new HashMap (); 796 getReferencedClasses(vendorID, orderedCmds, referenced, viewReferences); 797 } 798 799 800 819 private void getReferencedClasses(final String vendorID, final List orderedCmds, 820 final Set referenced, final Map viewReferences) 821 { 822 828 if (!referenced.contains(this)) 829 { 830 835 referenced.add(this); 836 837 Iterator iter = managedFields.iterator(); 838 839 while (iter.hasNext()) 840 { 841 FieldMetaData fmd = (FieldMetaData)iter.next(); 842 fmd.getReferencedClasses(vendorID, orderedCmds, referenced); 843 } 844 845 if (pcSuperclass != null) 846 { 847 forClass(pcSuperclass).getReferencedClasses(vendorID, orderedCmds, referenced); 848 } 849 850 if (identityClass != null) 851 { 852 ClassMetaData icmd = forClass(identityClass); 853 if (icmd != null) 854 icmd.getReferencedClasses(vendorID, orderedCmds, referenced); 855 } 856 857 if (getViewDefinition(vendorID) != null) 858 { 859 MacroString viewDef = new MacroString(this.clazz, getViewImports(), 860 getViewDefinition(vendorID)); 861 viewDef.substituteMacros(new MacroString.MacroHandler() 862 { 863 public void onIdentifierMacro(MacroString.IdentifierMacro im) 864 { 865 addViewReference(viewReferences, im.clazz); 866 forClass(im.clazz).getReferencedClasses(vendorID, orderedCmds, referenced, viewReferences); 867 } 868 869 public void onParameterMacro(MacroString.ParameterMacro pm) 870 { 871 throw new JDOUserException("Parameter macros not allowed in view definitions: " + pm); 872 } 873 } 874 ); 875 } 876 877 orderedCmds.add(this); 878 } 879 } 880 881 882 891 private void addViewReference(Map viewReferences, Class referenced) 892 throws JDOFatalUserException 893 { 894 if (this.clazz != referenced) 895 { 896 899 Set referencedSet = (Set )viewReferences.get(this.clazz); 900 if (null == referencedSet) 901 { 902 referencedSet = new HashSet (); 903 viewReferences.put(this.clazz, referencedSet); 904 } 905 906 referencedSet.add(referenced); 907 908 909 913 checkForCircularReferences(viewReferences, this.clazz, referenced, null); 914 } 915 } 916 917 918 928 private void checkForCircularReferences(Map viewReferences, Class referencer, 929 Class referencee, List referenceChain) 930 throws JDOFatalUserException 931 { 932 Set classes = (Set )viewReferences.get(referencee); 933 if (null != classes) 934 { 935 939 if (null == referenceChain) 940 { 941 referenceChain = new ArrayList (); 942 referenceChain.add(referencer); 943 } 944 referenceChain.add(referencee); 945 946 947 951 for (Iterator it=classes.iterator(); it.hasNext(); ) 952 { 953 Class current = (Class )it.next(); 954 955 if (current == referencer) 956 { 957 StringBuffer error = 958 new StringBuffer ("A circular dependency exists between views: "); 959 960 for (Iterator chainIter=referenceChain.iterator(); chainIter.hasNext(); ) 961 { 962 error.append(chainIter.next()); 963 if (chainIter.hasNext()) 964 { 965 error.append(" -> "); 966 } 967 } 968 969 throw new JDOFatalUserException(error.toString()); 970 } 971 else 972 { 973 977 checkForCircularReferences(viewReferences, referencer, 978 current, referenceChain); 979 } 980 } 981 } 982 } 983 } 984 | Popular Tags |