package org.openflexo.model; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.annotation.Nonnull; import org.openflexo.model.exceptions.ModelDefinitionException; public class ModelContext { public static class ModelPropertyXMLTag<I> { private final String tag; private final ModelProperty<? super I> property; private final ModelEntity<?> accessedEntity; public ModelPropertyXMLTag(ModelProperty<? super I> property) { super(); this.property = property; this.accessedEntity = null; this.tag = property.getXMLContext() + property.getXMLElement().xmlTag(); } public ModelPropertyXMLTag(ModelProperty<? super I> property, ModelEntity<?> accessedEntity) { super(); this.property = property; this.accessedEntity = accessedEntity; this.tag = property.getXMLContext() + accessedEntity.getXMLTag(); } public String getTag() { return tag; } public ModelProperty<? super I> getProperty() { return property; } public ModelEntity<?> getAccessedEntity() { return accessedEntity; } } private Map<Class, ModelEntity> modelEntities; private Map<String, ModelEntity> modelEntitiesByXmlTag; private Map<ModelEntity, Map<String, ModelPropertyXMLTag<?>>> modelPropertiesByXmlTag; private final Class<?> baseClass; public ModelContext(@Nonnull Class<?> baseClass) throws ModelDefinitionException { this.baseClass = baseClass; modelEntities = new HashMap<Class, ModelEntity>(); modelEntitiesByXmlTag = new HashMap<String, ModelEntity>(); modelPropertiesByXmlTag = new HashMap<ModelEntity, Map<String, ModelPropertyXMLTag<?>>>(); ModelEntity<?> modelEntity = ModelEntityLibrary.importEntity(baseClass); appendEntity(modelEntity, new HashSet<ModelEntity<?>>()); modelEntities = Collections.unmodifiableMap(modelEntities); modelEntitiesByXmlTag = Collections.unmodifiableMap(modelEntitiesByXmlTag); } public ModelContext(Class<?> baseClass, List<ModelContext> contexts) throws ModelDefinitionException { this.baseClass = baseClass; modelEntities = new HashMap<Class, ModelEntity>(); modelEntitiesByXmlTag = new HashMap<String, ModelEntity>(); modelPropertiesByXmlTag = new HashMap<ModelEntity, Map<String, ModelPropertyXMLTag<?>>>(); for (ModelContext context : contexts) { for (Entry<String, ModelEntity> e : context.modelEntitiesByXmlTag.entrySet()) { ModelEntity entity = modelEntitiesByXmlTag.put(e.getKey(), e.getValue()); // TODO: handle properly namespaces. Different namespaces allows to have identical tags // See also importModelEntity(Class<T>) if (entity != null && !entity.getImplementedInterface().equals(e.getValue().getImplementedInterface())) { throw new ModelDefinitionException(entity + " and " + e.getValue() + " declare the same XML tag but not the same implemented interface"); } } modelEntities.putAll(context.modelEntities); } if (baseClass != null) { ModelEntity<?> modelEntity = ModelEntityLibrary.importEntity(baseClass); appendEntity(modelEntity, new HashSet<ModelEntity<?>>()); } modelEntities = Collections.unmodifiableMap(modelEntities); modelEntitiesByXmlTag = Collections.unmodifiableMap(modelEntitiesByXmlTag); } public ModelContext(Class<?>... baseClasses) throws ModelDefinitionException { this(null, ModelContextLibrary.getModelContext(Arrays.asList(baseClasses))); } public ModelContext(ModelContext... contexts) throws ModelDefinitionException { this(null, contexts); } public ModelContext(Class<?> baseClass, ModelContext... contexts) throws ModelDefinitionException { this(baseClass, Arrays.asList(contexts)); } private void appendEntity(ModelEntity<?> modelEntity, Set<ModelEntity<?>> visited) throws ModelDefinitionException { visited.add(modelEntity); modelEntities.put(modelEntity.getImplementedInterface(), modelEntity); ModelEntity<?> put = modelEntitiesByXmlTag.put(modelEntity.getXMLTag(), modelEntity); if (put != null && put != modelEntity) { throw new ModelDefinitionException("Two entities define the same XMLTag '" + modelEntity.getXMLTag() + "'. Implemented interfaces: " + modelEntity.getImplementedInterface().getName() + " " + put.getImplementedInterface().getName()); } for (ModelEntity<?> e : modelEntity.getEmbeddedEntities()) { if (!visited.contains(e)) { appendEntity(e, visited); } } } public Class<?> getBaseClass() { return baseClass; } public ModelEntity<?> getModelEntity(String xmlElementName) { return modelEntitiesByXmlTag.get(xmlElementName); } public Iterator<ModelEntity> getEntities() { return modelEntities.values().iterator(); } public int getEntityCount() { return modelEntities.size(); } public <I> ModelEntity<I> getModelEntity(Class<I> implementedInterface) { return modelEntities.get(implementedInterface); } public <I> ModelPropertyXMLTag<I> getPropertyForXMLTag(ModelEntity<I> entity, String xmlTag) throws ModelDefinitionException { Map<String, ModelPropertyXMLTag<?>> tags = modelPropertiesByXmlTag.get(entity); if (tags == null) { modelPropertiesByXmlTag.put(entity, tags = new HashMap<String, ModelContext.ModelPropertyXMLTag<?>>()); Iterator<ModelProperty<? super I>> i = entity.getProperties(); while (i.hasNext()) { ModelProperty<? super I> property = i.next(); if (property.getXMLElement() != null) { ModelEntity accessedEntity = property.getAccessedEntity(); if (accessedEntity != null) { List<ModelEntity> allDescendantsAndMe = accessedEntity.getAllDescendantsAndMe(this); for (ModelEntity accessible : allDescendantsAndMe) { ModelPropertyXMLTag<I> tag = new ModelPropertyXMLTag<I>(property, accessible); ModelPropertyXMLTag<?> put = tags.put(tag.getTag(), tag); if (put != null) { throw new ModelDefinitionException("Property " + property + " defines a context which leads to an XMLElement name clash with " + accessible); } } } else if (StringConverterLibrary.getInstance().hasConverter(property.getType())) { ModelPropertyXMLTag<I> tag = new ModelPropertyXMLTag<I>(property); ModelPropertyXMLTag<?> put = tags.put(tag.getTag(), tag); if (put != null) { throw new ModelDefinitionException("Property " + property + " defines a context which leads to an XMLElement name clash with " + property.getType().getName()); } } } } } return (ModelPropertyXMLTag<I>) tags.get(xmlTag); } public List<ModelEntity<?>> getUpperEntities(Object object) { List<ModelEntity<?>> entities = new ArrayList<ModelEntity<?>>(); for (Class<?> i : object.getClass().getInterfaces()) { appendKnownEntities(entities, i); } return entities; } private void appendKnownEntities(List<ModelEntity<?>> entities, Class<?> i) { ModelEntity<?> modelEntity = getModelEntity(i); if (modelEntity != null && !entities.contains(i)) { entities.add(modelEntity); } else { for (Class<?> j : i.getInterfaces()) { appendKnownEntities(entities, j); } } } public String debug() { StringBuffer returned = new StringBuffer(); returned.append("*************** ModelContext ****************\n"); returned.append("Entities number: " + modelEntities.size() + "\n"); for (ModelEntity entity : modelEntities.values()) { returned.append("------------------- ").append(entity.getImplementedInterface().getSimpleName()) .append(" -------------------\n"); Iterator<ModelProperty> i; try { i = entity.getProperties(); } catch (ModelDefinitionException e) { e.printStackTrace(); continue; } while (i.hasNext()) { ModelProperty property = i.next(); returned.append(property.getPropertyIdentifier()).append(" ").append(property.getCardinality()).append(" type=") .append(property.getType().getSimpleName()).append("\n"); } } return returned.toString(); } }