package ecologylab.bigsemantics.metametadata; import java.util.ArrayList; import java.util.Collection; import ecologylab.bigsemantics.html.utils.StringBuilderUtils; import ecologylab.bigsemantics.metadata.MetadataClassDescriptor; import ecologylab.bigsemantics.metadata.MetadataFieldDescriptor; import ecologylab.bigsemantics.metametadata.MetaMetadataCompositeField.AttributeChangeListener; import ecologylab.bigsemantics.metametadata.declarations.MetaMetadataCollectionFieldDeclaration; import ecologylab.generic.HashMapArrayList; import ecologylab.generic.StringTools; import ecologylab.serialization.ClassDescriptor; import ecologylab.serialization.FieldDescriptor; import ecologylab.serialization.FieldType; import ecologylab.serialization.GenericTypeVar; import ecologylab.serialization.SimplTypesScope; import ecologylab.serialization.TranslationContext; import ecologylab.serialization.XMLTools; import ecologylab.serialization.annotations.simpl_inherit; import ecologylab.serialization.annotations.simpl_tag; import ecologylab.serialization.types.ScalarType; /** * A collection field. * * @author quyin */ @SuppressWarnings({ "rawtypes", "unchecked" }) @simpl_inherit @simpl_tag("collection") public class MetaMetadataCollectionField extends MetaMetadataCollectionFieldDeclaration { public static final String UNRESOLVED_NAME = "&UNRESOLVED_NAME"; /** * Each object in a collection of metadata require a specific MMdata composite object to be * associated with them. This is unavailable in the MMD XML, and must be generated when the XML is * read in. */ @Override public void deserializationPostHook(TranslationContext translationContext, Object object) { if (this.isInheritDone()) { return; } FieldType typeCode = this.getFieldType(); if (typeCode == FieldType.COLLECTION_SCALAR) { return; } createElementComposite(); } /** * for caching getTypeNameInJava(). */ private String typeNameInJava; public MetaMetadataCollectionField() { // no op } @Override public String getType() { return getChildType(); } @Override public String getExtendsAttribute() { return getChildExtends(); } public String getChildTag() { if (super.getChildTag() != null) return super.getChildTag(); if (super.getChildType() != null) return super.getChildType(); return null; } @Override public HashMapArrayList<String, MetaMetadataField> getChildrenMap() { HashMapArrayList<String, MetaMetadataField> kids = super.getChildrenMap(); return (kids != null && kids.size() > 0) ? kids.get(0).getChildrenMap() : null; } public MetaMetadataCompositeField getElementComposite() { HashMapArrayList<String, MetaMetadataField> kids = super.getChildrenMap(); return (kids != null && kids.size() > 0) ? (MetaMetadataCompositeField) kids.get(0) : null; } public MetaMetadataCompositeField getPreparedElementComposite() { MetaMetadataCompositeField elementComposite = getElementComposite(); prepareElementComposite(elementComposite); return elementComposite; } public MetaMetadataCompositeField createElementComposite() { HashMapArrayList<String, MetaMetadataField> kids = super.getChildrenMap(); String childType = getChildType(); String elementCompositeName = childType != null ? childType : UNRESOLVED_NAME; MetaMetadataCompositeField composite = new MetaMetadataCompositeField(elementCompositeName, kids); composite.setParent(this); composite.setEnclosingCollectionField(this); final MetaMetadataCollectionField thisField = this; composite.setAttributeChangeListener(new AttributeChangeListener() { @Override public void typeChanged(String newType) { thisField.setChildType(newType); } @Override public void extendsChanged(String newExtends) { thisField.setChildExtends(newExtends); } @Override public void tagChanged(String newTag) { if (thisField.getChildTag() == null) { thisField.setChildTag(newTag); } } @Override public void inlineMmdChanged(MetaMetadata newInlineMmd) { thisField.setInlineMmd(newInlineMmd); } @Override public void typeMmdChanged(MetaMetadata newTypeMmd) { thisField.setTypeMmd(newTypeMmd); } @Override public void scopeChanged(MmdScope newScope) { thisField.setScope(newScope); } }); kids.clear(); kids.put(composite.getName(), composite); prepareElementComposite(composite); return composite; } private void prepareElementComposite(MetaMetadataCompositeField composite) { if (composite.getName().equals(UNRESOLVED_NAME)) composite.setName(this.getChildType() == null ? this.getName() : this.getChildType()); composite.setRepository(this.getRepository()); composite.setPackageName(this.packageName()); composite.setDeclaringMmd(this.getDeclaringMmd()); composite.setPromoteChildren(this.isPromoteChildren()); // use set*Directly() to reduce unnecessary trigger of AttributeChangeListener composite.setTypeDirectly(this.getChildType()); composite.setExtendsAttributeDirectly(this.getChildExtends()); composite.setTagDirectly(this.getChildTag()); composite.setScopeDirectly(this.scope()); MetaMetadataCollectionField superField = (MetaMetadataCollectionField) this.getSuperField(); if (superField != null) { composite.setSuperField(superField.getElementComposite()); } } public boolean isCollectionOfScalars() { if (getChildScalarType() != null) { return true; } MetaMetadataField superField = getSuperField(); if (superField != null) { return ((MetaMetadataCollectionField) superField).isCollectionOfScalars(); } return false; } @Override protected String getTypeNameInJava() { String rst = typeNameInJava; if (rst == null) { String className = null; if (this.getFieldType() == FieldType.COLLECTION_SCALAR) { className = this.getChildScalarType().getJavaClass().getSimpleName(); } else { String typeName = getTypeName(); MetaMetadata mmd = getRepository().getMMByName(typeName); if (mmd != null && mmd.getType() != null && mmd.getExtendsAttribute() == null) { typeName = mmd.getType(); } className = XMLTools.classNameFromElementName(typeName); } rst = "List<" + className + ">"; typeNameInJava = rst; } return typeNameInJava; } @Override public String getTagForTypesScope() { // FIXME: seems broken when rewriting collection xpath without re-indicating child_type return getChildType() != null ? getChildType() : getTag() != null ? getTag() : getName(); } @Override public String resolveTag() { if (isNoWrap()) { return (getChildTag() != null) ? getChildTag() : getChildType(); } else { return (getTag() != null) ? getTag() : getName(); } } /** * Get the MetaMetadataCompositeField associated with this. * * @return this, because it is a composite itself. */ @Override public MetaMetadataCompositeField metaMetadataCompositeField() { return getPreparedElementComposite(); } @Override protected boolean isInlineDefinition() { return getElementComposite().isInlineDefinition(); } @Override protected MetadataClassDescriptor bindMetadataClassDescriptor(SimplTypesScope metadataTScope) { FieldType fieldType = getFieldType(); if (fieldType == FieldType.COLLECTION_ELEMENT) { MetaMetadataCompositeField childComposite = getElementComposite(); if (childComposite != null) return childComposite.bindMetadataClassDescriptor(metadataTScope); } return null; } @Override protected void customizeFieldDescriptor(SimplTypesScope metadataTScope, MetadataFieldDescriptorProxy fdProxy) { super.customizeFieldDescriptor(metadataTScope, fdProxy); fdProxy.setCollectionOrMapTagName(this.getChildTag()); fdProxy.setWrapped(!this.isNoWrap()); } @Override public MetadataFieldDescriptor findOrGenerateMetadataFieldDescriptor(SimplTypesScope tscope, MetadataClassDescriptor contextCd) { MetadataFieldDescriptor fd = this.getMetadataFieldDescriptor(); if (fd == null) { String tagName = this.resolveTag(); String fieldName = this.getFieldNameInJava(false); String javaTypeName = this.getTypeNameInJava(); boolean wrapped = !this.isNoWrap(); if (!wrapped) { tagName = null; } String genericTypeName = null; GenericTypeVar genericTypeVar = new GenericTypeVar(); FieldType typeCode = this.getFieldType(); switch (typeCode) { case COLLECTION_ELEMENT: { MetaMetadata inheritedMmd = this.getTypeMmd(); assert inheritedMmd != null : "IMPOSSIBLE: inheritedMmd == null: something wrong in the inheritance process!"; inheritedMmd.findOrGenerateMetadataClassDescriptor(tscope); MetadataClassDescriptor fieldCd = inheritedMmd.getMetadataClassDescriptor(); assert fieldCd != null : "IMPOSSIBLE: fieldCd == null: something wrong in the inheritance process!"; fd = new MetadataFieldDescriptor(this, tagName, this.getComment(), typeCode, fieldCd, contextCd, fieldName, null, null, javaTypeName); genericTypeName = fieldCd.getDescribedClassSimpleName(); if (StringTools.isUpperCase(getChildType())) { genericTypeVar.setName(getChildType()); } else { genericTypeVar.setClassDescriptor(fieldCd); } Collection<MmdGenericTypeVar> metaMetadataGenericTypeVars = this.getGenericTypeVarsList(); if (metaMetadataGenericTypeVars != null && metaMetadataGenericTypeVars.size() > 0) { for (MmdGenericTypeVar mmdgtv : metaMetadataGenericTypeVars) { GenericTypeVar gtv = new GenericTypeVar(); gtv.setName(mmdgtv.getArg()); // FIXME this is just one case. genericTypeVar.addGenericTypeVarArg(gtv); } } break; } case COLLECTION_SCALAR: { if (this.getChildrenSize() > 0) warning("Ignoring nested fields inside " + this + " because child_scalar_type specified ..."); ScalarType scalarType = this.getChildScalarType(); if (scalarType != null && getChildTag() == null) { String name = this.getName(); setChildTag(name.endsWith("s") ? name.substring(0, name.length() - 1) : name); warning("child_tag is necessary when using collection of scalars! will use a default child_tag: " + getChildTag()); } fd = new MetadataFieldDescriptor( this, tagName, this.getComment(), typeCode, null, contextCd, fieldName, scalarType, null, javaTypeName); genericTypeName = scalarType.getCSharpTypeName(); ClassDescriptor<? extends FieldDescriptor> cd = ClassDescriptor.getClassDescriptor(scalarType.getJavaClass()); genericTypeVar.setClassDescriptor(cd); break; } default: { error("Unrecognized field type " + typeCode + " for " + this); break; } } fd.setWrapped(wrapped); fd.setGeneric("<" + genericTypeName + ">"); ArrayList<GenericTypeVar> derivedGenericTypeVariables = new ArrayList<GenericTypeVar>(); derivedGenericTypeVariables.add(genericTypeVar); fd.setGenericTypeVars(derivedGenericTypeVariables); } this.setMetadataFieldDescriptor(fd); return fd; } @Override public void recursivelyRestoreChildComposite() { FieldType typeCode = this.getFieldType(); if (typeCode == FieldType.COLLECTION_SCALAR) return; if (getChildrenSize() == 1) { MetaMetadataCompositeField childComposite = getElementComposite(); if (childComposite != null) { HashMapArrayList<String, MetaMetadataField> childsKids = childComposite.getChildrenMap(); this.setChildrenMap(childsKids); if (childsKids != null) for (MetaMetadataField field : childsKids) { field.setParent(this); if (field instanceof MetaMetadataNestedField) ((MetaMetadataNestedField) field).recursivelyRestoreChildComposite(); } } return; } warning("collection field without a (correct) child composite: " + this); } @Override protected String getFingerprintString() { StringBuilder sb = StringBuilderUtils.acquire(); sb.append(super.getFingerprintString()); addToFp(sb, getChildTag()); addToFp(sb, getChildType()); addToFp(sb, getChildExtends()); addToFp(sb, getChildScalarType()); addToFp(sb, isNoWrap()); String fp = sb.toString(); StringBuilderUtils.release(sb); return fp; } }