/** * Copyright 2004-2016 Riccardo Solmi. All rights reserved. * This file is part of the Whole Platform. * * The Whole Platform is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Whole Platform 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>. */ package org.whole.lang.commons.model; import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.whole.lang.commons.reflect.CommonsFeatureDescriptorEnum; import org.whole.lang.factories.GenericEntityFactory; import org.whole.lang.matchers.GenericMatcher; import org.whole.lang.model.AbstractListCompositeEntity; import org.whole.lang.model.EnumValue; import org.whole.lang.model.IEntity; import org.whole.lang.model.adapters.IEntityAdapter; import org.whole.lang.reflect.EntityDescriptor; import org.whole.lang.reflect.EntityKinds; import org.whole.lang.reflect.FeatureDescriptor; import org.whole.lang.util.DataTypeUtils; import org.whole.lang.util.EntityUtils; import org.whole.lang.util.StringUtils; /** * @author Riccardo Solmi */ @SuppressWarnings("serial") public abstract class AbstractEntityResolver extends AbstractListCompositeEntity<org.whole.lang.model.IEntity> implements IEntityAdapter {//TODO remove IEntityAdapter private Map<FeatureDescriptor, IEntity> namedFeaturesMap = new HashMap<FeatureDescriptor, IEntity>(); private transient Set<EntityDescriptor<?>> assignableSet = null; public IEntity wShallowClone() { AbstractEntityResolver entity = (AbstractEntityResolver) super.wShallowClone(); entity.namedFeaturesMap = new HashMap<FeatureDescriptor, IEntity>();//entity.namedFeaturesMap); if (assignableSet != null) entity.assignableSet = null;//FIXME new HashSet<EntityDescriptor<?>>(entity.assignableSet); return entity; } @Override public int wSize() { if (namedFeaturesMap.isEmpty()) return super.wSize(); else return namedFeaturesMap.size(); } @Override public IEntity wGet(int index) { if (index < namedFeaturesMap.size()) return wGet(getFeatureDescriptorArray()[index]); else return super.wGet(index); } public IEntity wGetAdaptee(boolean force) { return this; } public void wSetAdaptee(IEntity implementor) { } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); } protected boolean hasAssignableSet() { return true; } protected Set<EntityDescriptor<?>> getAssignableSet() { if (assignableSet == null) { assignableSet = calculateAssignableSet(); //filter abstract types Iterator<EntityDescriptor<?>> i = assignableSet.iterator(); while (i.hasNext()) if (i.next().isAbstract()) i.remove(); } return assignableSet; } protected Set<EntityDescriptor<?>> calculateAssignableSet() { return wGetEntityDescriptor().getLanguageConcreteSubtypes(); } protected void updateAssignableSet() { assignableSet = null; if (!namedFeaturesMap.isEmpty()) { for (FeatureDescriptor fd : namedFeaturesMap.keySet()) { IEntity result = wResolveWith(fd); if (result != this) return; } } else if (!elements.isEmpty()) { for (IEntity element : elements) { IEntity result = wResolveWithComposite(element); if (result != this) return; } } } public final EntityKinds wGetEntityKind() { return EntityKinds.SIMPLE;//WAS RESOLVER; } public void wAccept(GenericMatcher matcher, IEntity other) { matcher.matchEntityResolver(this, other); } public <T extends IEntity> T wResolveWith(EntityDescriptor<T> ed) { T newChild = GenericEntityFactory.instance.create(ed); if (ed.getEntityKind().isComposite()) { for (IEntity element : elements) newChild.wAdd(EntityUtils.clone(element)); } else { Set<FeatureDescriptor> features = new HashSet<FeatureDescriptor>( newChild.wGetEntityDescriptor().getEntityFeatureDescriptors()); features.retainAll(namedFeaturesMap.keySet()); for (FeatureDescriptor fd : features) newChild.wSet(fd, EntityUtils.clone(this.wGet(fd))); } wGetParent().wSet(this, newChild); //FIXME This class should delegate to the newChild // IEntity adapter = wGetParent().wGet(this); //No longer returns adapter // wGetParent().wSet(adapter, newChild); // // if (adapter instanceof IEntityAdapter) // ((IEntityAdapter) adapter).wSetAdaptee(newChild); return newChild; } protected IEntity wResolveWith(FeatureDescriptor feature) { if (hasAssignableSet()) { Iterator<EntityDescriptor<?>> i = getAssignableSet().iterator(); while (i.hasNext()) if (!i.next().has(feature)) i.remove(); if (getAssignableSet().size() == 1) { EntityDescriptor<?> descriptor = getAssignableSet().iterator().next(); return wResolveWith(descriptor); } else if (getAssignableSet().isEmpty()) throw new IllegalArgumentException("The feature "+feature.getName()+" is incompatible with this entity."); } return this; } //FIXME add a field for storing a data entity when two or more data entity descriptors are available protected IEntity wResolveWith(Class<?> type) { if (hasAssignableSet()) { Iterator<EntityDescriptor<?>> i = getAssignableSet().iterator(); while (i.hasNext()) { EntityDescriptor<?> ed = i.next(); Class<?> dataType = ed.getDataType(); if (dataType == null || !dataType.isAssignableFrom(type)) i.remove(); } if (getAssignableSet().size() == 1) { EntityDescriptor<?> descriptor = getAssignableSet().iterator().next(); return wResolveWith(descriptor); } else if (getAssignableSet().isEmpty()) throw new IllegalArgumentException("The feature "+type.getName()+" is incompatible with this entity."); } return this; } protected IEntity wResolveWithComposite(IEntity child) { if (hasAssignableSet()) { EntityDescriptor<?> childED = child.wGetEntityDescriptor(); Iterator<EntityDescriptor<?>> i = getAssignableSet().iterator(); while (i.hasNext()) { EntityDescriptor<?> ed = i.next(); if (!ed.getEntityKind().isComposite() || !ed.getEntityDescriptor(0).isPlatformSupertypeOf(childED)) i.remove(); } if (getAssignableSet().size() == 1) { EntityDescriptor<?> descriptor = getAssignableSet().iterator().next(); return wResolveWith(descriptor); } else if (getAssignableSet().isEmpty()) throw new IllegalArgumentException("The child "+childED+" is incompatible with this entity."); } return this; } public Class<?> wGetType(IEntity child) { return child.getClass(); } public boolean wAdd(int index, IEntity child) { IEntity result = wResolveWithComposite(child); if (result == this) return super.wAdd(index, child); else return result.wAdd(index, child); } public boolean wAdd(IEntity child) { IEntity result = wResolveWithComposite(child); if (result == this) return super.wAdd(child); else return result.wAdd(child); } @Override protected IEntity adaptElement(IEntity child) { return child; } public FeatureDescriptor wGetFeatureDescriptor(int index) { if (index < namedFeaturesMap.size()) return getFeatureDescriptorArray()[index]; else return CommonsFeatureDescriptorEnum.composite_element; } public Set<FeatureDescriptor> getFeatureDescriptorSet() { return namedFeaturesMap.keySet(); } public FeatureDescriptor[] getFeatureDescriptorArray() { return namedFeaturesMap.keySet().toArray(new FeatureDescriptor[namedFeaturesMap.size()]); } public boolean wContains(FeatureDescriptor feature) { return namedFeaturesMap.containsKey(feature); } public IEntity wGet(FeatureDescriptor feature) { Object value = namedFeaturesMap.get(feature); if (value == null || !(value instanceof IEntity)) throw new IllegalArgumentException("Feature return type error: "+feature.getName()); return (IEntity) value; } public void wSet(FeatureDescriptor feature, IEntity value) { IEntity result = wResolveWith(feature); if (result == this) { namedFeaturesMap.put(feature, null);//FIXME default value; if (!wContains(feature)) throw new IllegalArgumentException("feature: "+feature.getName()+" is not defined"); notifyChanged(feature, (IEntity) namedFeaturesMap.put(feature, value), value); } else result.wSet(feature, value); } public boolean wBooleanValue() { return notifyRequested(false); } public byte wByteValue() { return notifyRequested((byte) 0); } public char wCharValue() { return notifyRequested('c'); } public double wDoubleValue() { return notifyRequested(0d); } public float wFloatValue() { return notifyRequested(0f); } public int wIntValue() { return notifyRequested(0); } public long wLongValue() { return notifyRequested(0l); } public short wShortValue() { return notifyRequested((short) 0); } public String wStringValue() { return notifyRequested(""); } public Date wDateValue() { return notifyRequested((java.util.Date) null); } public EnumValue wEnumValue() { return notifyRequested((EnumValue) null); } public Object wGetValue() { return notifyRequested((Object) null); } public void wSetValue(boolean value) { wResolveWith(boolean.class).wSetValue(value); } public void wSetValue(byte value) { wResolveWith(byte.class).wSetValue(value); } public void wSetValue(char value) { wResolveWith(char.class).wSetValue(value); } public void wSetValue(double value) { wResolveWith(double.class).wSetValue(value); } public void wSetValue(float value) { wResolveWith(float.class).wSetValue(value); } public void wSetValue(int value) { wResolveWith(int.class).wSetValue(value); } public void wSetValue(long value) { wResolveWith(long.class).wSetValue(value); } public void wSetValue(short value) { wResolveWith(short.class).wSetValue(value); } public void wSetValue(String value) { wResolveWith(String.class).wSetValue(value); } public void wSetValue(Date value) { wResolveWith(Date.class).wSetValue(value); } public void wSetValue(EnumValue value) { wResolveWith(value.getClass()).wSetValue(value); } public void wSetValue(Object value) { Class<?> valueClass = value.getClass(); if (valueClass.isPrimitive() || value instanceof String || value instanceof Date || value instanceof EnumValue) wResolveWith(valueClass).wSetValue(value); else if (StringUtils.isWrapper(valueClass.getName())) wResolveWith(DataTypeUtils.unboxFilter(valueClass)).wSetValue(value); else wResolveWith(Object.class).wSetValue(value); } public boolean wIsSet() { return false; } public void wUnset() { } public void wUnset(FeatureDescriptor feature) { namedFeaturesMap.remove(feature); } public void wUnset(int index) { super.wUnset(index);//FIXME } }