/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.persistence; import java.awt.Dimension; import java.awt.Insets; import java.awt.Point; import java.io.Serializable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import org.json.JSONException; import com.servoy.j2db.persistence.ContentSpec.Element; import com.servoy.j2db.persistence.StaticContentSpecLoader.TypedProperty; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.JSONWrapperMap; import com.servoy.j2db.util.Pair; import com.servoy.j2db.util.PersistHelper; import com.servoy.j2db.util.ServoyJSONObject; import com.servoy.j2db.util.UUID; import com.servoy.j2db.util.Utils; /** * Abstract base class used by all IPersist classes If a sub class implements ISupportChild\IPersistConeble more methods are already provided * * @author jblok */ @SuppressWarnings("nls") public abstract class AbstractBase implements IPersist { private static final long serialVersionUID = 1L; private static final String[] OVERRIDE_PATH = new String[] { "override" }; //$NON-NLS-1$ public static final int DEFAULT_INT = ContentSpec.ZERO.intValue(); // == ContentSpec.getJavaClassMemberDefaultValue(IRepository.INTEGER) /* * Attributes for IPersist */ protected UUID uuid; protected int element_id; protected int revision = 1; protected boolean isChanged = true; protected ISupportChilds parent; protected int type; /* * All 1-n providers for this class */ private List<IPersist> allobjects = null; private transient Map<UUID, IPersist> allobjectsMap = null; private Map<String, Object> propertiesMap = new HashMap<String, Object>(); private Map<String, Object> bufferPropertiesMap = null; /* * Attributes, do not change default values do to repository default_textual_classvalue */ protected transient JSONWrapperMap jsonCustomProperties = null; /* * _____________________________________________________________ Declaration and definition of constructors */ public AbstractBase(int type, ISupportChilds parent, int element_id, UUID uuid) { this.type = type; this.parent = parent; this.element_id = element_id; this.uuid = uuid; } public Map<String, Object> getPropertiesMap() { return new HashMap<String, Object>(propertiesMap); } public void clearProperty(String propertyName) { if (propertiesMap.containsKey(propertyName) || jsonCustomProperties != null && jsonCustomProperties.containsKey(propertyName)) { isChanged = true; // call the setter with content spec default so any cached data is cleared Element element = StaticContentSpecLoader.getContentSpec().getPropertyForObjectTypeByName(getTypeID(), propertyName); setProperty(propertyName, element == null ? null : element.getDefaultClassValue()); if (propertiesMap.containsKey(propertyName)) { propertiesMap.remove(propertyName); } else if (jsonCustomProperties != null && jsonCustomProperties.containsKey(propertyName)) { jsonCustomProperties.remove(propertyName); } if (bufferPropertiesMap != null) { bufferPropertiesMap.remove(propertyName); // the setProperty above might set (wrongly) default value in bufferPropertiesMap as well during import } } } public boolean hasProperty(String propertyName) { return propertiesMap.containsKey(propertyName) || (bufferPropertiesMap != null && bufferPropertiesMap.containsKey(propertyName)); } public void copyPropertiesMap(Map<String, Object> newProperties, boolean overwriteMap) { if (overwriteMap && propertiesMap.size() > 0) { // remove properties that are not in newProperties for (String key : propertiesMap.keySet().toArray(new String[propertiesMap.size()])) { if (newProperties == null || !newProperties.containsKey(key)) { clearProperty(key); } } } // apply the new properties if (newProperties != null) { try { startBufferUseForProperties(); Iterator<Entry<String, Object>> iterator = newProperties.entrySet().iterator(); while (iterator.hasNext()) { Entry<String, Object> next = iterator.next(); Object v = next.getValue(); if (v instanceof ServoyJSONObject) { v = ((ServoyJSONObject)v).clone(); } setProperty(next.getKey(), v); } } finally { applyPropertiesBuffer(); } } } public void setProperty(String propertyName, Object val) { try { Map<String, Method> methods = RepositoryHelper.getSettersViaIntrospection(this); if (methods.containsKey(propertyName)) { methods.get(propertyName).invoke(this, new Object[] { val }); } else { Debug.error("No introspection method found for property:" + propertyName + ", on persist " + toString()); //$NON-NLS-1$//$NON-NLS-2$ } } catch (Exception ex) { Debug.error(ex); } } /** * @param propertyName * @param val */ private void setPropertyInternal(String propertyName, Object val) { Boolean newPropAndWasChanged = null; if (propertiesMap.containsKey(propertyName)) { if (!StaticContentSpecLoader.PROPERTY_NAME.getPropertyName().equals(propertyName)) { checkForChange(propertiesMap.get(propertyName), val); } else { checkForNameChange((String)propertiesMap.get(propertyName), (String)val); } } else { newPropAndWasChanged = Boolean.valueOf(isChanged); isChanged = true; } if (bufferPropertiesMap != null) { bufferPropertiesMap.put(propertyName, val); } else { if (!hasProperty(StaticContentSpecLoader.PROPERTY_EXTENDSID.getPropertyName()) || (this instanceof ISupportExtendsID && Utils.equalObjects(Integer.valueOf(((ISupportExtendsID)this).getExtendsID()), StaticContentSpecLoader.getContentSpec().getPropertyForObjectTypeByName(getTypeID(), StaticContentSpecLoader.PROPERTY_EXTENDSID.getPropertyName()).getDefaultClassValue()))) { Element element = StaticContentSpecLoader.getContentSpec().getPropertyForObjectTypeByName(getTypeID(), propertyName); if (element != null && Utils.equalObjects(val, element.getDefaultClassValue())) { if (newPropAndWasChanged != null) { // changed was set because property was added, now we remove property again, revert to previous changed state isChanged = newPropAndWasChanged.booleanValue(); } propertiesMap.remove(propertyName); return; } } propertiesMap.put(propertyName, val); } } public void startBufferUseForProperties() { if (bufferPropertiesMap == null) { bufferPropertiesMap = new HashMap<String, Object>(); } } public void applyPropertiesBuffer() { if (bufferPropertiesMap != null) { Map<String, Object> tempMap = new HashMap<String, Object>(bufferPropertiesMap); bufferPropertiesMap = null; // apply extendsId first if (tempMap.containsKey(StaticContentSpecLoader.PROPERTY_EXTENDSID.getPropertyName())) { setPropertyInternal(StaticContentSpecLoader.PROPERTY_EXTENDSID.getPropertyName(), tempMap.get(StaticContentSpecLoader.PROPERTY_EXTENDSID.getPropertyName())); tempMap.remove(StaticContentSpecLoader.PROPERTY_EXTENDSID.getPropertyName()); } for (String propertyName : tempMap.keySet()) { setPropertyInternal(propertyName, tempMap.get(propertyName)); } } } public Object getProperty(String propertyName) { Object value = null; if (bufferPropertiesMap != null && bufferPropertiesMap.containsKey(propertyName)) { value = bufferPropertiesMap.get(propertyName); } else if (propertiesMap.containsKey(propertyName)) { value = propertiesMap.get(propertyName); } else if (!StaticContentSpecLoader.PROPERTY_CUSTOMPROPERTIES.getPropertyName().equals(propertyName) && this instanceof ISupportExtendsID && PersistHelper.isOverrideElement((ISupportExtendsID)this)) { IPersist superPersist = PersistHelper.getSuperPersist((ISupportExtendsID)this); if (superPersist != null) { return ((AbstractBase)superPersist).getProperty(propertyName); } Element element = StaticContentSpecLoader.getContentSpec().getPropertyForObjectTypeByName(getTypeID(), propertyName); if (element != null) value = element.getDefaultClassValue(); } else { // content spec default value Element element = StaticContentSpecLoader.getContentSpec().getPropertyForObjectTypeByName(getTypeID(), propertyName); if (element != null) value = element.getDefaultClassValue(); } if (value instanceof Insets) { return new Insets(((Insets)value).top, ((Insets)value).left, ((Insets)value).bottom, ((Insets)value).right); } if (value instanceof Dimension) { return new Dimension((Dimension)value); } if (value instanceof Point) { return new Point((Point)value); } return value; } @SuppressWarnings("unchecked") <T> T getTypedProperty(TypedProperty<T> property) { return (T)getProperty(property.getPropertyName()); } protected void setTypedProperty(TypedProperty<Integer> property, int value) { setPropertyInternal(property.getPropertyName(), Integer.valueOf(value)); } protected void setTypedProperty(TypedProperty<Boolean> property, boolean value) { setPropertyInternal(property.getPropertyName(), Boolean.valueOf(value)); } <T> void setTypedProperty(TypedProperty<T> property, T value) { if (value instanceof Integer) { Integer intValue = (Integer)value; if (property == StaticContentSpecLoader.PROPERTY_TABSEQ && intValue.intValue() < 1 && intValue.intValue() != ISupportTabSeq.DEFAULT && intValue.intValue() != ISupportTabSeq.SKIP) { return;//irrelevant value from editor } if (property == StaticContentSpecLoader.PROPERTY_ROTATION && intValue.intValue() > 360) { return; } } if (value instanceof String && property.getPropertyName().toLowerCase().endsWith("datasource")) //$NON-NLS-1$ { setPropertyInternal(property.getPropertyName(), ((String)value).intern()); } else { setPropertyInternal(property.getPropertyName(), value); } } <T> void clearTypedProperty(TypedProperty<T> property) { if (!propertiesMap.containsKey(property.getPropertyName())) { return; } isChanged = true; propertiesMap.remove(property.getPropertyName()); } /* * _____________________________________________________________ Methods from IPersist */ public Object acceptVisitor(IPersistVisitor visitor) { Object retval = visitor.visit(this); if (retval == IPersistVisitor.CONTINUE_TRAVERSAL && this instanceof ISupportChilds) { Iterator<IPersist> it = getAllObjects(); while ((retval == IPersistVisitor.CONTINUE_TRAVERSAL || retval == IPersistVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER) && it.hasNext()) { retval = it.next().acceptVisitor(visitor); } } return (retval == IPersistVisitor.CONTINUE_TRAVERSAL || retval == IPersistVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER || retval == IPersistVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_UP) ? null : retval; } public Object acceptVisitorDepthFirst(IPersistVisitor visitor) throws RepositoryException { Object retval = IPersistVisitor.CONTINUE_TRAVERSAL; if (this instanceof ISupportChilds) { Iterator<IPersist> it = getAllObjects(); while (it.hasNext() && (retval == IPersistVisitor.CONTINUE_TRAVERSAL || retval == IPersistVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER || retval == IPersistVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_UP)) { IPersist visitee = it.next(); retval = visitee.acceptVisitorDepthFirst(visitor); } } if (retval == IPersistVisitor.CONTINUE_TRAVERSAL || retval == IPersistVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER) { retval = visitor.visit(this); } return (retval == IPersistVisitor.CONTINUE_TRAVERSAL || retval == IPersistVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER || retval == IPersistVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_UP) ? null : retval; } void clearParent() { parent = null; } public void setParent(ISupportChilds parent) { this.parent = parent; } public int getID() { return element_id; } /* * only called when cloning to set the clone on a new id. */ public void setID(int id) { element_id = id; } public void setRevisionNumber(int revision) { this.revision = revision; clearChanged(); } public int getRevisionNumber() { return revision; } public boolean isChanged() { return isChanged; } public void flagChanged() { this.isChanged = true; } public void clearChanged() { isChanged = false; setRuntimeProperty(NameChangeProperty, null); } public IRootObject getRootObject() { return parent.getRootObject(); } public ISupportChilds getParent() { return parent; } /* * _____________________________________________________________ Methods from ISupportChilds only visible when subclasses implement ISupportChilds */ public void removeChild(IPersist obj) { internalRemoveChild(obj); if (getRootObject().getChangeHandler() != null) { getRootObject().getChangeHandler().fireIPersistRemoved(obj); } } protected void internalRemoveChild(IPersist obj) { if (allobjects != null) { allobjects.remove(obj); if (allobjectsMap != null && obj != null) { allobjectsMap.remove(obj.getUUID()); } } } public void addChild(IPersist obj) { internalAddChild(obj); if (getRootObject().getChangeHandler() != null) { getRootObject().getChangeHandler().fireIPersistCreated(obj); } if (obj instanceof AbstractBase && this instanceof ISupportChilds) { ((AbstractBase)obj).setParent((ISupportChilds)this); } } public void internalAddChild(IPersist obj) { if (allobjects == null) { allobjects = Collections.synchronizedList(new ArrayList<IPersist>(3)); } allobjects.add(obj); if (allobjectsMap != null && obj != null) { allobjectsMap.put(obj.getUUID(), obj); } } public <T extends IPersist> Iterator<T> getObjects(int tp) { return new TypeIterator<T>(getAllObjectsAsList(), tp); } public Iterator<IPersist> getAllObjects() { return getAllObjectsAsList().iterator(); } public List<IPersist> getAllObjectsAsList() { return allobjects == null ? Collections.<IPersist> emptyList() : Collections.unmodifiableList(allobjects); } public void internalClearAllObjects() { allobjects = null; allobjectsMap = null; } private void flushAllObjectsMap() { allobjectsMap = null; } public IPersist getChild(UUID childUuid) { if (allobjectsMap == null && allobjects != null && allobjects.size() > 0) { allobjectsMap = new ConcurrentHashMap<UUID, IPersist>(allobjects.size(), 0.9f, 16); for (IPersist persist : allobjects) { if (persist != null) { allobjectsMap.put(persist.getUUID(), persist); } } } return allobjectsMap == null ? null : allobjectsMap.get(childUuid); } public Iterator<IPersist> getAllObjects(Comparator< ? super IPersist> c) { return Utils.asSortedIterator(getAllObjects(), c); } /** * @see java.lang.Object#equals(Object) */ @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj != null && obj.getClass() == getClass()) { AbstractBase abstractBase = (AbstractBase)obj; if (abstractBase.getUUID().equals(uuid)) { if (getParent() != null && abstractBase.getParent() != null) { return getParent().equals(abstractBase.getParent()); } return true; } } return false; } private final int hashCodeNr = new Object().hashCode(); /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { // Use separate hashcode object so hashCode() is stable for cloned object, when used as key in map a clone should match. return hashCodeNr; } protected void checkForNameChange(String oldValue, String newValue) { if (oldValue == null && newValue == null) return; checkForChange(oldValue, newValue); if ((oldValue == null && newValue != null) || !oldValue.equals(newValue)) { if (getRuntimeProperty(NameChangeProperty) == null) //only do once, first time { if (oldValue == null) { setRuntimeProperty(NameChangeProperty, ""); //$NON-NLS-1$ } else { setRuntimeProperty(NameChangeProperty, oldValue); } } } } public static final RuntimeProperty<String> NameChangeProperty = new RuntimeProperty<String>() { }; public static final SerializableRuntimeProperty<HashMap<UUID, Integer>> UUIDToIDMapProperty = new SerializableRuntimeProperty<HashMap<UUID, Integer>>() { private static final long serialVersionUID = 1L; }; public static final SerializableRuntimeProperty<HashMap<String, String>> UnresolvedPropertyToValueMapProperty = new SerializableRuntimeProperty<HashMap<String, String>>() { private static final long serialVersionUID = 1L; }; private void checkForChange(Object oldValue, Object newValue) { if (isChanged) return;//no need to check boolean retval = false; //null checks if (oldValue == null) { retval = (newValue != null); } else { retval = (!oldValue.equals(newValue)); } if (retval) isChanged = true; } /** * @see java.lang.Object#clone() */ public final IPersist clonePersist() { AbstractBase cloned; try { cloned = (AbstractBase)clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } fillClone(cloned); return cloned; } protected void fillClone(AbstractBase cloned) { cloned.allobjectsMap = null; cloned.propertiesMap = new HashMap<String, Object>(); cloned.copyPropertiesMap(getPropertiesMap(), true); if (cloned.allobjects != null) { cloned.allobjects = Collections.synchronizedList(new ArrayList<IPersist>(allobjects.size())); for (IPersist persist : allobjects) { if (persist instanceof ICloneable) { IPersist clonePersist = ((ICloneable)persist).clonePersist(); cloned.addChild(clonePersist); // cloned.allobjects.add(clonePersist); } else { cloned.allobjects.add(persist); } } } } /** * Make a clone of the current obj (also makes new repository entry) * * @return a clone from this object */ public IPersist cloneObj(ISupportChilds newParent, boolean deep, IValidateName validator, boolean changeName, boolean changeChildNames, boolean flattenOverrides) throws RepositoryException { ChangeHandler changeHandler = null; if (newParent != null) { changeHandler = newParent.getRootObject().getChangeHandler(); } else { changeHandler = getRootObject().getChangeHandler(); } if (changeHandler == null) { throw new RepositoryException("cannot clone/copy without change handler"); //$NON-NLS-1$ } AbstractBase clone = (AbstractBase)changeHandler.cloneObj(this, newParent, flattenOverrides); if (changeName && clone instanceof ISupportUpdateableName && ((ISupportUpdateableName)clone).getName() != null) { int random = new Random().nextInt(1024); String newName = ((ISupportUpdateableName)clone).getName() + "_copy" + random; //$NON-NLS-1$ ((ISupportUpdateableName)clone).updateName(validator, newName); } if (clone instanceof ISupportChilds) //do deep clone { clone.allobjectsMap = null; if (deep && allobjects != null) { clone.allobjects = Collections.synchronizedList(new ArrayList<IPersist>(allobjects.size())); Iterator<IPersist> it = Collections.unmodifiableList(this.allobjects).iterator(); while (it.hasNext()) { IPersist element = it.next(); if (element instanceof IPersistCloneable) { ((IPersistCloneable)element).cloneObj((ISupportChilds)clone, deep, validator, changeChildNames, changeChildNames, flattenOverrides); } } } else { clone.allobjects = null;//clear so they are not shared due to native clone ! } } return clone; } public int getTypeID() { return type; } public UUID getUUID() { return uuid; } public void resetUUID() { resetUUID(null); } public void resetUUID(UUID uuidParam) { if (parent instanceof AbstractBase) { ((AbstractBase)parent).flushAllObjectsMap(); } if (uuidParam == null) uuid = UUID.randomUUID(); else uuid = uuidParam; } public IPersist getAncestor(int typeId) { if (getTypeID() == typeId) { return this; } if (parent == null) { return null; } return parent.getAncestor(typeId); } public MetaData getMetaData() { return null; } public static <T> T selectByName(Iterator<T> iterator, String name) { return selectByProperty(iterator, StaticContentSpecLoader.PROPERTY_NAME, name); } public static <T, P> T selectByProperty(Iterator<T> iterator, TypedProperty<P> property, P value) { if (value == null || (value instanceof String && ((String)value).trim().length() == 0)) return null; while (iterator.hasNext()) { T n = iterator.next(); if (n instanceof AbstractBase && value.equals(((AbstractBase)n).getProperty(property.getPropertyName()))) { return n; } } return null; } public static <T extends IPersist> T selectById(Iterator<T> iterator, int id) { while (iterator.hasNext()) { T p = iterator.next(); if (p.getID() == id) { return p; } } return null; } /** * Set the customProperties * * <b>Note: this call is only for (de)serialisation, use putCustomProperty to set specific custom properties </b> * * @param arg the customProperties * @throws JSONException */ public void setCustomProperties(String arg) { setTypedProperty(StaticContentSpecLoader.PROPERTY_CUSTOMPROPERTIES, arg); jsonCustomProperties = null; } /** * Get the customProperties * * <b>Note: this call is only for (de)serialisation, use getCustomProperty to get specific custom properties </b> * * @return the customProperties */ public String getCustomProperties() { if (jsonCustomProperties != null) { return jsonCustomProperties.toString(); } return getTypedProperty(StaticContentSpecLoader.PROPERTY_CUSTOMPROPERTIES); } public Object getCustomProperty(String[] path) { IPersist persist = this; while (persist instanceof AbstractBase) { Object customProperty = ((AbstractBase)persist).getCustomPropertyLocal(path); if (customProperty != null) { return customProperty; } if (persist instanceof ISupportExtendsID) { persist = PersistHelper.getSuperPersist((ISupportExtendsID)persist); } else { break; } } return null; } @SuppressWarnings("unchecked") protected Object getCustomPropertyLocal(String[] path) { String customProperties = getTypedProperty(StaticContentSpecLoader.PROPERTY_CUSTOMPROPERTIES); if (customProperties == null) return null; if (jsonCustomProperties == null) { jsonCustomProperties = new JSONWrapperMap(customProperties); } try { Map<String, Object> map = jsonCustomProperties; for (int i = 0; i < path.length; i++) { if (map == null || !map.containsKey(path[i])) { return null; } Object node = ServoyJSONObject.toJava(map.get(path[i])); if (i == path.length - 1) { // leaf node return node; } map = (Map<String, Object>)node; } } catch (Exception ex) { Debug.error(ex); } return null; } @SuppressWarnings("unchecked") public Object putCustomProperty(String[] path, Object value) { String customProperties = getTypedProperty(StaticContentSpecLoader.PROPERTY_CUSTOMPROPERTIES); if (customProperties == null && value == null) return null; if (jsonCustomProperties == null) { if (customProperties != null) { jsonCustomProperties = new JSONWrapperMap(customProperties); } else { jsonCustomProperties = new JSONWrapperMap(new ServoyJSONObject()); } } Map<String, Object> map = jsonCustomProperties; for (int i = 0; i < path.length - 1; i++) { if (!map.containsKey(path[i])) { if (value == null) { return null; // value not found } map.put(path[i], new ServoyJSONObject()); } map = (Map<String, Object>)map.get(path[i]); } String leaf = path[path.length - 1]; Object old = null; if (value == null) { old = map.remove(leaf); if (map.isEmpty() && path.length > 1) { // remove empty map putCustomProperty(Utils.arraySub(path, 0, path.length - 1), null); } } else { old = map.put(leaf, value); } setTypedProperty(StaticContentSpecLoader.PROPERTY_CUSTOMPROPERTIES, jsonCustomProperties.toString()); return old; } public Map<String, Object> getCustomDesignTimeProperties() { Map<String, Object> map = (Map<String, Object>)getCustomProperty(new String[] { "design" }); if (map == null || map.size() == 0) { return null; } return map; } /** * Merge properties from this, super, ... * This will always return a non-null map of properties which is a map that can be written to. */ public Map<String, Object> getMergedCustomDesignTimeProperties() { return getMergedCustomDesignTimePropertiesInternal(new HashMap<String, Object>()); } private Map<String, Object> getMergedCustomDesignTimePropertiesInternal(Map<String, Object> mergedProperties) { if (this instanceof ISupportExtendsID) { IPersist superPersist = PersistHelper.getSuperPersist((ISupportExtendsID)this); if (superPersist instanceof AbstractBase) { ((AbstractBase)superPersist).getMergedCustomDesignTimePropertiesInternal(mergedProperties); } } Map<String, Object> map = getCustomDesignTimeProperties(); if (map != null) { mergedProperties.putAll(map); } return mergedProperties; } /** * Unmerge properties from this, super, ... by removing elements that are defined with equal value in super persist. */ public void setUnmergedCustomDesignTimeProperties(Map<String, Object> mergedProperties) { Map<String, Object> map = mergedProperties; if (map != null) { IPersist superPersist = this instanceof ISupportExtendsID ? PersistHelper.getSuperPersist((ISupportExtendsID)this) : null; if (superPersist instanceof AbstractBase) { Map<String, Object> superMergedProperties = ((AbstractBase)superPersist).getMergedCustomDesignTimeProperties(); if (superMergedProperties != null) { for (Entry<String, Object> superEntry : superMergedProperties.entrySet()) { Object subval = map.get(superEntry.getKey()); if (subval != null && subval.equals(superEntry.getValue())) { // unmerge values same as superpersist if (map == mergedProperties) { // make copy, do not modify original map = new HashMap<String, Object>(mergedProperties); } map.remove(superEntry.getKey()); } } } } } setCustomDesignTimeProperties(map); } public Map<String, Object> setCustomDesignTimeProperties(Map<String, Object> map) { return (Map<String, Object>)putCustomProperty(new String[] { "design" }, map); } public Object getCustomDesignTimeProperty(String key) { if (key != null) { return getCustomProperty(new String[] { "design", key }); //$NON-NLS-1$ } return null; } public Object putCustomDesignTimeProperty(String key, Object value) { if (key != null) { return putCustomProperty(new String[] { "design", key }, value); //$NON-NLS-1$ } return null; } public Map<String, Object> getCustomMobileProperties() { Map<String, Object> map = (Map<String, Object>)getCustomProperty(new String[] { "mobile" }); if (map == null || map.size() == 0) { return null; } return map; } public Map<String, Object> setCustomMobileProperties(Map<String, Object> map) { return (Map<String, Object>)putCustomProperty(new String[] { "mobile" }, map); } public Object getCustomMobileProperty(String key) { if (key != null) { return getCustomProperty(new String[] { "mobile", key }); //$NON-NLS-1$ } return null; } public Object putCustomMobileProperty(String key, Object value) { if (key != null) { return putCustomProperty(new String[] { "mobile", key }, value); //$NON-NLS-1$ } return null; } @SuppressWarnings("unchecked") public List<Object> getInstanceMethodArguments(String methodKey) { if (methodKey != null) { return (List<Object>)getCustomProperty(new String[] { "methods", methodKey, "arguments" }); //$NON-NLS-1$ //$NON-NLS-2$ } return null; } @SuppressWarnings("unchecked") public List<Object> putInstanceMethodArguments(String methodKey, List<Object> args) { if (methodKey != null) { return (List<Object>)putCustomProperty(new String[] { "methods", methodKey, "arguments" }, //$NON-NLS-1$//$NON-NLS-2$ args == null ? null : Collections.unmodifiableList(args)); } return null; } @SuppressWarnings("unchecked") public Pair<List<Object>, List<Object>> getInstanceMethodParametersLocal(String methodKey) { if (methodKey != null) { List<Object> params = (List<Object>)getCustomPropertyLocal(new String[] { "methods", methodKey, "parameters" }); //$NON-NLS-1$ //$NON-NLS-2$ List<Object> args = (List<Object>)getCustomPropertyLocal(new String[] { "methods", methodKey, "arguments" }); //$NON-NLS-1$ //$NON-NLS-2$ return new Pair<List<Object>, List<Object>>(params, args); } return null; } public List<Object> putInstanceMethodParameters(String methodKey, List<Object> paramNames, List<Object> args) { if (methodKey != null) { putCustomProperty(new String[] { "methods", methodKey, "parameters" }, args == null ? null : Collections.unmodifiableList(paramNames)); putCustomProperty(new String[] { "methods", methodKey, "arguments" }, args == null ? null : Collections.unmodifiableList(args)); } return null; } public boolean hasOverrideCustomProperty() { return (getCustomProperty(OVERRIDE_PATH) != null); } public void removeOverrideCustomProperty() { putCustomProperty(OVERRIDE_PATH, null); } /** Check if this object has any overriding properties left. * @return */ public boolean hasOverrideProperties() { for (Entry<String, Object> entry : propertiesMap.entrySet()) { if (!StaticContentSpecLoader.PROPERTY_EXTENDSID.getPropertyName().equals(entry.getKey())) { // a non override property return true; } } // nothing else found return false; } /* Runtime properties */ /** Application level meta data. */ private PropertyEntry[] properties; public <T extends Serializable> T getSerializableRuntimeProperty(SerializableRuntimeProperty<T> property) { return property.get(properties); } public <T extends Serializable> void setSerializableRuntimeProperty(SerializableRuntimeProperty<T> property, T object) { properties = property.set(properties, object); } private transient PropertyEntry[] transient_properties; public <T extends Object> T getRuntimeProperty(RuntimeProperty<T> property) { return property.get(transient_properties); } public <T extends Object> void setRuntimeProperty(RuntimeProperty<T> property, T object) { transient_properties = property.set(transient_properties, object); } }