@Override public AttrType getAttrType(String attr) { PropertyDefinition attrDef = getAttrDefinition(attr); if (attrDef == null) { if (Attribute.isFinalAttribute(attr)) { return Attribute.splitType("string"); } return null; } return Attribute.splitType(attrDef.getType()); }
/** * An attribute is valid if declared in an ancestor, and not set in an ancestor. Check if the * value is conforming to its type (string, int, boolean). Internal attribute (associated with a * field) cannot be set. Must be called on the level above. * * <p>Checks if attr is correctly defined for component ent it must be explicitly defined in its * upper groups, for the top group, it must be already existing. * * <p>boolean Forced = true can be set only by Apform. Needed when an internal attribute is set in * the program, Apform propagates the value to the attribute. * * @param attr * @param value * @return */ public PropertyDefinition validDef(String attr, boolean forced) { if (Attribute.isFinalAttribute(attr)) { logger.error("In " + this + ", cannot redefine final attribute \"" + attr + "\""); return null; } if (Attribute.isReservedAttributePrefix(attr)) { logger.error("In " + this + ", attribute\"" + attr + "\" is reserved"); return null; } PropertyDefinition definition = this.getAttrDefinition(attr); if (definition == null) { logger.error("In " + this + ", attribute \"" + attr + "\" is undefined."); return null; } /* * Internal field attributes cannot be set */ if (definition.getInjected() == InjectedPropertyPolicy.INTERNAL && !forced) { logger.error( "In " + this + ", attribute \"" + attr + "\" is an internal field attribute and cannot be set."); return null; } /* * if the same attribute exists above, it is a redefinition. */ ComponentImpl group = (ComponentImpl) this.getGroup(); if (group != null && group.get(attr) != null) { Object groupValue = group.get(attr); String defaultValue = definition.getDefaultValue(); boolean isDefault = defaultValue != null && groupValue.equals( Attribute.checkAttrType(attr, defaultValue, definition.getType())); // If the attribute above is the default value, it is allowed to change it if (!isDefault && !Attribute.isBuiltAttribute(attr)) { logger.error("In " + this + ", cannot redefine attribute \"" + attr + "\""); return null; } } return definition; }
/** * Warning: to be used only by Apform for setting internal attributes. Only Inhibits the message * "Attribute " + attr + " is an internal field attribute and cannot be set."); * * @param attr * @param value * @param forced * @return */ public boolean setProperty(String attr, Object value, boolean forced) { /* * Validate that the property is defined and the value is valid Forced * means that we can set field attribute */ PropertyDefinition def = validDef(attr, forced); if (def == null) { return false; } // At initialization, all valid attributes are ok for specs Object val = Attribute.checkAttrType(attr, value, def.getType()); if (val == null) { return false; } /* * Force recalculation of dependencies that may have been invalidated by * the property change. This must be done before notification and * propagation, otherwise we risk to remove links updated by managers. * * We remove only those links that are now invalid problematic for those * that are not lazy links : assychronous messages, .. and avoiding * unnecessary work TODO Check if this must be done for all links or * only dynamic links */ Object oldValue = get(attr); put(attr, val); for (Link incoming : getInvLinks()) { // If still valid, do nothing if (incoming.hasConstraints() && !incoming.isValid()) { incoming.remove(); } } // If outgoing constraints have substitution, the link may be now invalid for (Link outgoing : getLocalLinks()) { // if (!!!((RelationDefinitionImpl) outgoing.getRelDefinition()).isStaticImplemConstraints() // && !outgoing.isValid()) { outgoing.getRelToResolve().reComputeSubstFilters(); if (!outgoing.isValid()) { outgoing.remove(); } } // WARNING : undo the change because propagate needs the initial state if (oldValue == null) { remove(attr); } else { put(attr, oldValue); } // does the change, notifies managers, changes the platform and // propagate to members this.propagate(attr, val); return true; }
@Override public Map<String, String> getAllPropertiesString() { Map<String, String> ret = new HashMap<String, String>(); for (Entry<String, Object> e : this.entrySet()) { if (!Attribute.isFinalAttribute(e.getKey())) { ret.put(e.getKey(), Util.toStringAttrValue(e.getValue())); } } return ret; }
/** * Warning: to be used only by Apform for removing internal attributes. Only Inhibits the message * "Attribute " + attr + " is an program field attribute and cannot be removed."); */ public boolean removeProperty(String attr, boolean forced) { String oldValue = getProperty(attr); if (oldValue == null) { logger.error("ERROR: \"" + attr + "\" not instanciated"); return false; } if (Attribute.isFinalAttribute(attr)) { logger.error("ERROR: \"" + attr + "\" is a final attribute"); return false; } if (Attribute.isReservedAttributePrefix(attr)) { logger.error("ERROR: \"" + attr + "\" is a reserved attribute"); return false; } PropertyDefinition propDef = getAttrDefinition(attr); if (propDef != null && propDef.getField() != null && !forced) { logger.error( "In " + this + " attribute " + attr + " is a program field and cannot be removed."); return false; } if (getGroup() != null && getGroup().getProperty(attr) != null) { logger.error("In " + this + " attribute " + attr + " inherited and cannot be removed."); return false; } // it is ok, remove it and propagate to members, recursively propagateRemove(attr); // TODO. Should we notify at all levels ? ApamManagers.notifyAttributeRemoved(this, attr, oldValue); return true; }
/** * To be called once the Apam entity is fully initialized. * * <p>Computes all its attributes, including inheritance. Checks if initial properties are * consistent with the declarations. * * <p>NOTE this method is also called when the owner changes, to force recalculation of * substitutions that depend on the current owner. */ private void initializeProperties(Map<String, String> initialProperties) { Component group = getGroup(); /* * Currently the instance declaration may include invalid properties. * * For declared instances this should not happen as the declaration is validated at build-time. For dynamically * created instances (using the APAM API or the apform API directly) this should be validated by this method. * * However, there are many properties added by the iPOJO apform layer that need to be ignored, so we simply * silently ignore all invalid properties. * * TODO We should be able to distinguish properties specified by the user that must be validated, from properties * used internally by the iPOJO apform. */ Set<String> invalidDeclaredProperties = new HashSet<String>(); for (String property : getDeclaration().getProperties().keySet()) { boolean isDefined = getDeclaration().isDefined(property) || (group != null && group.getPropertyDefinition(property) != null); if (!isDefined) { invalidDeclaredProperties.add(property); } if (group != null && group.getProperty(property) != null) { invalidDeclaredProperties.add(property); } } getDeclaration().getProperties().keySet().removeAll(invalidDeclaredProperties); /* * Merge initial and declared properties. * */ Map<String, String> fullInitialProperties = new HashMap<String, String>(getDeclaration().getProperties()); if (initialProperties != null) { fullInitialProperties.putAll(initialProperties); fullInitialProperties.remove("instance.name"); } /* * NOTE In the case of change owner, the initial properties include inherited values from the group, * we ignore them to avoid false error messages, they will be added later by the normal inheritance * mechanism * * TODO Distinguish the case of change owner from a real initialization */ if (initialProperties != null && group != null) { for (Map.Entry<String, String> initialProperty : initialProperties.entrySet()) { if (group.getProperty(initialProperty.getKey()) != null && group.getProperty(initialProperty.getKey()).equals(initialProperty.getValue())) { fullInitialProperties.remove(initialProperty.getKey()); } } } // start cleaning the properties (normally empty) clear(); /* * First add the valid attributes. */ for (Map.Entry<String, String> initialProperty : fullInitialProperties.entrySet()) { PropertyDefinition def = validDef(initialProperty.getKey(), true); if (def != null) { Object val = Attribute.checkAttrType( initialProperty.getKey(), initialProperty.getValue(), def.getType()); if (val != null) { put(initialProperty.getKey(), val); } } } /* * then add those coming from its group, avoiding overloads. */ if (group != null) { for (String attr : group.getAllProperties().keySet()) { if (get(attr) == null) { put(attr, ((ComponentImpl) group).get(attr)); } } } /* * Add the default values specified in the group for properties not explicitly specified */ if (group != null) { for (PropertyDefinition definition : group.getDeclaration().getPropertyDefinitions()) { if (definition.hasDefaultValue() && get(definition.getName()) == null && definition.getInjected() != InjectedPropertyPolicy.INTERNAL) { Object val = Attribute.checkAttrType( definition.getName(), definition.getDefaultValue(), definition.getType()); if (val != null) { put(definition.getName(), val); } } } } /* * Set the attribute for the final attributes */ put(CST.SHARED, Boolean.toString(isShared())); put(CST.SINGLETON, Boolean.toString(isSingleton())); put(CST.INSTANTIABLE, Boolean.toString(isInstantiable())); /* * Finally add the specific attributes. * Should be the only place where instanceof is used. */ put(CST.NAME, apform.getDeclaration().getName()); if (this instanceof Specification) { put(CST.SPECNAME, apform.getDeclaration().getName()); } else if (this instanceof Implementation) { put(CST.IMPLNAME, apform.getDeclaration().getName()); if (this instanceof CompositeType) { put(CST.APAM_COMPOSITETYPE, CST.V_TRUE); } } else if (this instanceof Instance) { put(CST.INSTNAME, apform.getDeclaration().getName()); if (this instanceof Composite) { Composite composite = (Composite) this; put(CST.APAM_COMPOSITE, CST.V_TRUE); if (composite.getMainInst() != null) { put(CST.APAM_MAIN_INSTANCE, composite.getMainInst().getName()); } } } /* * and propagate, to the platform and to members, in case the spec has been created after the implem */ for (Map.Entry<String, Object> entry : this.entrySet()) { for (Component member : getMembers()) { ((ComponentImpl) member).propagate(entry.getKey(), entry.getValue()); } } }