/** * Adds an object to the contents of this AbstractReferenceManufacturer. This is used in * conditions where this AbstractReferenceManufacturer was not used to construct the object. * * <p>Implementation Note: There are various situations where this "external construction" may * happen - the primary one being loading of "game mode" information like CDOMStat objects. * * @param item The object to be imported into this AbstractReferenceManufacturer * @param key The identifier of the object to be imported into this AbstractReferenceManufacturer * @throws IllegalArgumentException if the given object is not of the Class that this * AbstractReferenceManufacturer constructs and references */ @Override public void addObject(T item, String key) { if (!factory.isMember(item)) { throw new IllegalArgumentException( "Attempted to register a " + item.getClass().getName() + " in " + factory.getReferenceDescription()); } T current = active.get(key); if (current == null) { active.put(key, item); } else { duplicates.addToListFor(new CaseInsensitiveString(key), item); } }
/** * Remove the given object from this AbstractReferenceManufacturer. Returns true if the object was * removed from this AbstractReferenceManufacturer; false otherwise. * * @param item The object to be removed from this AbstractReferenceManufacturer. * @return true if the object was removed from this AbstractReferenceManufacturer; false * otherwise. */ @Override public boolean forgetObject(T item) { if (!factory.isMember(item)) { throw new IllegalArgumentException( "Object to be forgotten does not match Class " + "of this AbstractReferenceManufacturer"); } String key = active.getKeyFor(item); if (key == null) { /* * TODO This is a bug - the key name is not necessarily loaded into * the object, it may have been consumed by the object context... :P */ CaseInsensitiveString ocik = new CaseInsensitiveString(item.getKeyName()); duplicates.removeFromListFor(ocik, item); } else { CaseInsensitiveString ocik = new CaseInsensitiveString(key); List<T> list = duplicates.getListFor(ocik); if (list == null) { // No replacement active.remove(key); } else { T newActive = duplicates.getElementInList(ocik, 0); duplicates.removeFromListFor(ocik, newActive); active.put(key, newActive); } } return true; }
/** * Gets the object represented by the given identifier. Will return null if an object with the * given identifier is not present in this AbstractReferenceManufacturer. * * <p>Note that this is testing *object* presence. This will not return an object if a reference * for the given identifier has been requested; it will only return true if an object with the * given identifier has actually been constructed by or imported into this * AbstractReferenceManufacturer. * * @param key identifier of the object to be returned * @return The object stored in this AbstractReferenceManufacturer with the given identifier, or * null if this AbstractReferenceManufacturer does not contain an object with the given * identifier. */ @Override public T getObject(String key) { T po = active.get(key); if (po != null) { List<T> list = duplicates.getListFor(new CaseInsensitiveString(key)); if ((list != null) && !list.isEmpty()) { Logging.errorPrint( "Reference to Constructed " + factory.getReferenceDescription() + " " + key + " is ambiguous"); StringBuilder sb = new StringBuilder(1000); sb.append("Locations: "); sb.append(po.getSourceURI()); for (T dupe : list) { sb.append(", "); sb.append(dupe.getSourceURI()); } Logging.errorPrint(sb.toString()); } return po; } return null; }
/** * Returns a CDOMGroupRef for the given Class or Class/Context provided by this * AbstractReferenceManufacturer. * * @return A CDOMGroupRef which is intended to contain all the objects of the Class or * Class/Context this AbstractReferenceManufacturer represents. */ @Override public CDOMGroupRef<T> getAllReference() { if (allRef == null) { allRef = factory.getAllReference(); } return allRef; }
private boolean resolveGroupReferences() { for (T obj : getAllObjects()) { if (allRef != null) { allRef.addResolution(obj); } for (Map.Entry<FixedStringList, WeakReference<CDOMGroupRef<T>>> me : typeReferences.entrySet()) { CDOMGroupRef<T> trt = me.getValue().get(); if (trt != null) { boolean typeOkay = true; for (String type : me.getKey()) { if (!obj.isType(type)) { typeOkay = false; break; } } if (typeOkay) { trt.addResolution(obj); } } } } if (allRef != null && allRef.getObjectCount() == 0) { Logging.errorPrint( "Error: No " + factory.getReferenceDescription() + " objects were loaded but were referred to in the data"); fireUnconstuctedEvent(allRef); return false; } return true; }
/** * Constructs a new Loadable of the Class or Class/Category represented by this * AbstractReferenceManufacturer * * <p>This should remain protected (vs. public) as it is for "internal use only"; it serves as a * convenience method to wrap the .newInstance call and the possible Exceptions. Other classes * should use constructObject(String) * * @param key The identifier of the Loadable to be constructed * @return The new Loadable of the Class or Class/Category represented by this * AbstractReferenceManufacturer * @throws IllegalArgumentException if the given identifier is null or empty (length is zero) */ @Override public T buildObject(String key) { if (key == null || key.equals("")) { throw new IllegalArgumentException("Cannot build empty name"); } T obj = factory.newInstance(); obj.setName(key); return obj; }
private boolean resolvePrimitiveReferences(UnconstructedValidator validator) { boolean resolutionSuccessful = true; for (Entry<String, WeakReference<CDOMSingleRef<T>>> me1 : referenced.entrySet()) { CDOMSingleRef<T> value = me1.getValue().get(); if (value != null) { resolutionSuccessful &= factory.resolve(this, me1.getKey(), value, validator); } } return resolutionSuccessful; }
@Override public ObjectContainer<T> convertObjectContainer(String typeStr) { /* * TODO Really needs to do a Primitive search... :/ */ if (typeStr.startsWith("TYPE=") || typeStr.startsWith("TYPE.")) { return factory.getTypeReference(typeStr.substring(5).split("\\.")); } throw new IllegalArgumentException("ObjectContainer does not support: " + typeStr); }
private boolean validateDuplicates() { boolean returnGood = true; for (CaseInsensitiveString second : duplicates.getKeySet()) { List<T> list = duplicates.getListFor(second); T good = active.get(second.toString()); /* * CONSIDER Should get CDOMObject reference out of here :( */ if (good instanceof CDOMObject) { CDOMObject cdo = (CDOMObject) good; for (int i = 0; i < list.size(); i++) { T dupe = list.get(i); if (cdo.isCDOMEqual((CDOMObject) dupe)) { for (Iterator<WeakReference<T>> it = manufactured.iterator(); it.hasNext(); ) { WeakReference<T> wr = it.next(); T mfg = wr.get(); if (mfg == null) { it.remove(); } // Yes this is instance equality, not .equals else if (mfg == good) { forgetObject(good); break; } } } } } if (duplicates.containsListFor(second)) { Logging.errorPrint( "More than one " + factory.getReferenceDescription() + " with key/name " + good.getKeyName() + " was built"); List<T> dupes = duplicates.getListFor(second); StringBuilder sb = new StringBuilder(1000); sb.append("Sources: "); sb.append(good.isInternal() ? "<internal>" : good.getSourceURI()); for (T dupe : dupes) { sb.append(", ").append(dupe.isInternal() ? "<internal>" : dupe.getSourceURI()); if (!dupe.getKeyName().equals(good.getKeyName())) { Logging.errorPrint("Key case differed for " + dupe.getKeyName()); } } Logging.errorPrint(sb.toString()); returnGood = false; } } return returnGood; }
/** * Resolves the references that have been requested from this AbstractReferenceManufacturer, using * the objects contained within this AbstractReferenceManufacturer. * * <p>This method guarantees that all references are resolved. * * <p>Note: Implementations of AbstractReferenceManufacturer may place limits on the number of * times resolveReferences() can be called. The reason for this is that some references may only * be resolved once, and the AbstractReferenceManufacturer is not required to maintain a list of * references that have been resolved and those which have not been resolved. */ @Override public boolean resolveReferences(UnconstructedValidator validator) { boolean resolutionSuccessful = resolvePrimitiveReferences(validator); resolutionSuccessful &= resolveGroupReferences(); for (WeakReference<CDOMGroupRef<T>> ref : typeReferences.values()) { CDOMGroupRef<T> trt = ref.get(); if (trt != null && trt.getObjectCount() == 0) { Logging.errorPrint( "Error: No " + factory.getReferenceDescription() + " objects of " + trt.getLSTformat(false) + " were loaded but were referred to in the data"); fireUnconstuctedEvent(trt); resolutionSuccessful = false; } } isResolved = true; return resolutionSuccessful; }
/** * Gets a reference to the Class or Class/Context provided by this AbstractReferenceManufacturer. * The reference will be a reference to the objects identified by the given types. * * @param types An array of the types of objects to which the returned CDOMReference will refer. * @return A CDOMGroupRef which is intended to contain objects of a given Type for the Class or * Class/Context this AbstractReferenceManufacturer represents. * @throws IllegalArgumentException if any of the given Strings is null, empty (length is zero), * or contains a period (.), equals (=), comma (,) or pipe (|) */ @Override public CDOMGroupRef<T> getTypeReference(String... types) { for (String type : types) { if (type == null || type.length() == 0) { throw new IllegalArgumentException( "Attempt to acquire empty Type " + "(the type String contains a null or empty element)"); } if (type.indexOf('.') != -1) { throw new IllegalArgumentException( "Cannot build Reference with type conaining a period: " + type); } if (type.indexOf('=') != -1) { throw new IllegalArgumentException( "Cannot build Reference with type conaining an equals: " + type); } if (type.indexOf(',') != -1) { throw new IllegalArgumentException( "Cannot build Reference with type conaining a comma: " + type); } if (type.indexOf('|') != -1) { throw new IllegalArgumentException( "Cannot build Reference with type conaining a pipe: " + type); } } Arrays.sort(types); FixedStringList typeList = new FixedStringList(types); WeakReference<CDOMGroupRef<T>> ref = typeReferences.get(typeList); if (ref != null) { CDOMGroupRef<T> trt = ref.get(); if (trt != null) { return trt; } } // Didn't find the appropriate key, create new CDOMGroupRef<T> cgr = factory.getTypeReference(types); typeReferences.put(typeList, new WeakReference<CDOMGroupRef<T>>(cgr)); return cgr; }
private boolean validateNames() { if (!Logging.isLoggable(Logging.LST_WARNING)) { return true; } for (String key : active.keySet()) { T value = active.get(key); if (value.isInternal()) { continue; } /* * http://wiki.pcgen.org/index.php?title=Data_LST_Standards * * Characters which should never be used in object names are Commas * (,), Pipes (|), Backslashes (\), Colons (:), Semicolons (;), * Periods (.), Brackets ([]), Percent (%), Asterisk (*) and Equals * (=). */ if (key.indexOf(',') != -1 && factory.getReferenceClass() != RollMethod.class) { Logging.log( Logging.LST_WARNING, "Found " + factory.getReferenceDescription() + " with KEY: " + key + " which contains a comma " + "(prohibited character in a key)"); } if (key.indexOf('|') != -1) { Logging.log( Logging.LST_WARNING, "Found " + factory.getReferenceDescription() + " with KEY: " + key + " which contains a pipe " + "(prohibited character in a key)"); } if (key.indexOf('\\') != -1) { Logging.log( Logging.LST_WARNING, "Found " + factory.getReferenceDescription() + " with KEY: " + key + " which contains a backslash " + "(prohibited character in a key)"); } if (key.indexOf(':') != -1) { Logging.log( Logging.LST_WARNING, "Found " + factory.getReferenceDescription() + " with KEY: " + key + " which contains a colon " + "(prohibited character in a key)"); } if (key.indexOf(';') != -1) { Logging.log( Logging.LST_WARNING, "Found " + factory.getReferenceDescription() + " with KEY: " + key + " which contains a semicolon " + "(prohibited character in a key)"); } // if (key.indexOf('.') != -1) // { // Logging.log(Logging.LST_WARNING, "Found " // + getReferenceDescription() + " with KEY: " + key // + " which contains a period " // + "(prohibited character in a key)"); // } if (key.indexOf('%') != -1) { Logging.log( Logging.LST_WARNING, "Found " + factory.getReferenceDescription() + " with KEY: " + key + " which contains a percent sign " + "(prohibited character in a key)"); } if (key.indexOf('*') != -1) { Logging.log( Logging.LST_WARNING, "Found " + factory.getReferenceDescription() + " with KEY: " + key + " which contains an asterisk " + "(prohibited character in a key)"); } if (key.indexOf('=') != -1) { Logging.log( Logging.LST_WARNING, "Found " + factory.getReferenceDescription() + " with KEY: " + key + " which contains an equals sign " + "(prohibited character in a key)"); } if ((key.indexOf('[') != -1) || (key.indexOf(']') != -1)) { Logging.log( Logging.LST_WARNING, "Found " + factory.getReferenceDescription() + " with KEY: " + key + " which contains a bracket " + "(prohibited character in a key)"); } } return true; }
/** * Gets a reference to the Class or Class/Context provided by this AbstractReferenceManufacturer. * The reference will be a reference to the object identified by the given key. * * @param key The key used to identify the object to which the returned CDOMReference will refer. * @return A CDOMReference that refers to the object identified by the given key * @throws IllegalArgumentException if the given key is null or empty */ @Override public CDOMSingleRef<T> getReference(String key) { /* * TODO This is incorrect, but a hack for now :) * * Mainly this throws around IllegalArgumentException in order to catch * bad parsing issues (design flaws in the code). Not sure if we want to * continue that long term? Once tokens are truly tested this may not be * necessary or desirable. */ if (key == null) { throw new IllegalArgumentException("Cannot request a reference to null identifier"); } if (key.length() == 0) { throw new IllegalArgumentException("Cannot request a reference to an empty identifier"); } /* * Items thrown below this point are for protection from coding errors * in LST files, not part of the public interface of this method */ try { Integer.parseInt(key); throw new IllegalArgumentException("A number cannot be a valid single item: " + key); } catch (NumberFormatException nfe) { // ok } if (key.contains("=")) { throw new IllegalArgumentException( "= cannot be a in valid single item (perhaps something like TYPE= " + "is not supported in this token?): " + key); } if (key.equalsIgnoreCase("ANY")) { throw new IllegalArgumentException( "Any cannot be a valid single item (not supported in this token?)"); } if (key.equalsIgnoreCase("ALL")) { throw new IllegalArgumentException( "All cannot be a valid single item (not supported in this token?)"); } if (key.contains(":")) { throw new IllegalArgumentException( ": cannot exist in a valid single item (did you try to use a " + "PRE where it is not supported?) " + key); } if (key.equalsIgnoreCase("%LIST")) { throw new IllegalArgumentException( "%LIST cannot be a valid single item (not supported in this token?)"); } WeakReference<CDOMSingleRef<T>> wr = referenced.get(key); if (wr != null) { CDOMSingleRef<T> ref = wr.get(); if (ref != null) { return ref; } } CDOMSingleRef<T> ref; if (isResolved) { T current = active.get(key); if (current == null) { throw new IllegalArgumentException( key + " is not valid post-resolution " + "because it was never constructed"); } ref = CDOMDirectSingleRef.getRef(current); } else { CDOMSingleRef<T> lr = factory.getReference(key); referenced.put(key, new WeakReference<CDOMSingleRef<T>>(lr)); ref = lr; } return ref; }
/** * The class of object this AbstractReferenceManufacturer represents. * * @return The class of object this AbstractReferenceManufacturer represents. */ @Override public Class<T> getReferenceClass() { return factory.getReferenceClass(); }
@Override public Indirect<T> convertIndirect(String key) { return factory.getReference(key); }
@Override public String getReferenceDescription() { return factory.getReferenceDescription(); }