package ecologylab.bigsemantics.metametadata;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import ecologylab.bigsemantics.Utils;
import ecologylab.bigsemantics.html.utils.StringBuilderUtils;
import ecologylab.bigsemantics.metadata.MetadataClassDescriptor;
import ecologylab.bigsemantics.metadata.MetadataFieldDescriptor;
import ecologylab.bigsemantics.metadata.mm_name;
import ecologylab.bigsemantics.metadata.semantics_mixin;
import ecologylab.bigsemantics.metametadata.declarations.MetaMetadataNestedFieldDeclaration;
import ecologylab.bigsemantics.metametadata.exceptions.MetaMetadataException;
import ecologylab.bigsemantics.metametadata.fieldparsers.FieldParserElement;
import ecologylab.generic.HashMapArrayList;
import ecologylab.serialization.ClassDescriptor;
import ecologylab.serialization.FieldType;
import ecologylab.serialization.MetaInformation;
import ecologylab.serialization.SimplTypesScope;
import ecologylab.serialization.annotations.simpl_composite;
import ecologylab.serialization.annotations.simpl_inherit;
import ecologylab.serialization.annotations.simpl_tag;
import ecologylab.serialization.formatenums.StringFormat;
/**
* A nested field.
*
* @author quyin
*/
@SuppressWarnings({ "rawtypes" })
@simpl_inherit
public abstract class MetaMetadataNestedField extends MetaMetadataNestedFieldDeclaration
implements PackageSpecifier
{
public static final String POLYMORPHIC_CLASSES_SEP = ",";
@simpl_composite
@simpl_tag("field_parser")
private FieldParserElement fieldParserElement;
@simpl_composite
@mm_dont_inherit
private MmdScope scope;
private boolean mmdScopeTraversed;
public MetaMetadataNestedField()
{
super();
}
public MetaMetadataNestedField(String name, HashMapArrayList<String, MetaMetadataField> set)
{
super(name, set);
}
public MetaMetadataNestedField(MetaMetadataField copy, String name)
{
super(copy, name);
}
public String packageName()
{
return this.getPackageName();
}
public FieldParserElement getFieldParserElement()
{
return fieldParserElement;
}
public MmdScope getScope()
{
return scope;
}
public MmdScope scope()
{
if (scope == null)
{
scope = new MmdScope(this.toString());
// also put all local GTVs into the scope
Map<String, MmdGenericTypeVar> gtvScope = getGenericTypeVars();
if (gtvScope != null)
{
for (String localGtvName : gtvScope.keySet())
{
MmdGenericTypeVar gtv = gtvScope.get(localGtvName);
scope.put(localGtvName, gtv);
gtv.scope().addAncestor(scope);
}
}
}
return scope;
}
public void setScope(MmdScope scope)
{
this.scope = scope;
}
/**
* Get the MetaMetadataCompositeField associated with this.
*
* @return
*/
public abstract MetaMetadataCompositeField metaMetadataCompositeField();
@Override
public String getTypeName()
{
String result = null;
if (this instanceof MetaMetadataCompositeField)
{
MetaMetadataCompositeField mmcf = (MetaMetadataCompositeField) this;
if (mmcf.getType() != null)
result = mmcf.getType();
}
else if (this instanceof MetaMetadataCollectionField)
{
MetaMetadataCollectionField mmcf = (MetaMetadataCollectionField) this;
if (mmcf.getChildType() != null)
result = mmcf.getChildType();
else if (mmcf.getChildScalarType() != null)
result = mmcf.getChildScalarType().getJavaTypeName();
}
if (result == null)
{
MetaMetadataField inherited = getSuperField();
if (inherited != null)
{
// use inherited field's type
result = inherited.getTypeName();
}
}
if (result == null)
{
// defining new type inline without using type= / child_type=
result = getName();
}
return result;
}
protected String getMetadataClassName()
{
return this.getTypeMmd().getMetadataClassName();
}
/**
*
* @return the corresponding Metadata class simple name.
*/
protected String getMetadataClassSimpleName()
{
MetaMetadata inheritedMmd = this.getTypeMmd();
return inheritedMmd.getMetadataClassSimpleName();
}
/**
* to determine if this field is polymorphic inherently, that is, a field which we don't have
* prior knowledge of its specific meta-metadata type when its encompassing meta-metadata is used.
* <p />
* NOTE THAT this is different from {@code isPolymorphicInDescendantFields()} which determines if
* this field is used for extended types in descendant fields. in that case although the field is
* polymorphic, too, but we can determine the specific meta-metadata type for this field if the
* encompassing meta-metadata is used.
*
* @return
* @see isPolymorphicInDescendantFields
*/
public boolean isPolymorphicInherently()
{
String polymorphicScope = getPolymorphicScope();
String polymorphicClasses = getPolymorphicClasses();
return (polymorphicScope != null && polymorphicScope.length() > 0)
|| (polymorphicClasses != null && polymorphicClasses.length() > 0);
}
/**
* Determine if there is an inline meta-metadata defined by this field.
*
* @return
*/
abstract protected boolean isInlineDefinition();
/**
* bind metadata field descriptors to sub-fields of this nested field, with field names as keys,
* but without mixins field.
* <p>
* sub-fields that lack corresponding field descriptors will be removed from this nested field.
* <p>
* note that this field no longer uses a boolean flag to prevent multiple invocation. this should
* have been done by the bindClassDescriptor() method.
*
* @param metadataTScope
* the translation scope of (generated) metadata classes.
* @param metadataClassDescriptor
* the metadata class descriptor where field descriptors can be found.
*/
protected void bindMetadataFieldDescriptors(SimplTypesScope metadataTScope,
MetadataClassDescriptor metadataClassDescriptor)
{
// copy the kids collection first to prevent modification to the collection during iteration
// (which may invalidate the iterator).
List<MetaMetadataField> fields = new ArrayList<MetaMetadataField>(this.getChildren());
for (MetaMetadataField thatChild : fields)
{
// look up by field name and bind
MetadataFieldDescriptor metadataFieldDescriptor = thatChild
.bindMetadataFieldDescriptor(metadataTScope, metadataClassDescriptor);
if (metadataFieldDescriptor == null)
{
warning("Cannot bind metadata field descriptor for " + thatChild
+ ", class descriptor is " + metadataClassDescriptor);
this.getChildrenMap().remove(thatChild.getName());
continue;
}
metadataFieldDescriptor.setDefiningMmdField(thatChild);
// recursively process sub-fields
FieldType fieldType = metadataFieldDescriptor.getType();
if (fieldType == FieldType.COMPOSITE_ELEMENT || fieldType == FieldType.COLLECTION_ELEMENT)
{
if (thatChild.hasChildren())
{
// bind class descriptor for nested sub-fields
MetaMetadataNestedField nested = (MetaMetadataNestedField) thatChild;
MetadataClassDescriptor elementClassDescriptor =
((MetaMetadataNestedField) thatChild).bindMetadataClassDescriptor(metadataTScope);
if (elementClassDescriptor != null)
{
MetaMetadata mmdForThatChild = nested.getTypeMmd();
if (mmdForThatChild != null && mmdForThatChild.getMetadataClassDescriptor() == null)
// mmdForThatChild.setMetadataClassDescriptor(elementClassDescriptor);
mmdForThatChild.bindMetadataClassDescriptor(metadataTScope);
}
else
{
warning("Cannot determine elementClassDescriptor for " + thatChild);
this.getChildrenMap().remove(thatChild.getName());
}
}
}
if (this instanceof MetaMetadata)
{
MetaMetadata mmd = (MetaMetadata) this;
String naturalId = thatChild.getAsNaturalId();
if (naturalId != null)
{
mmd.addNaturalIdField(naturalId, thatChild);
}
}
}
}
/**
* bind metadata class descriptor to this nested field. bind field descriptors to nested
* sub-fields inside this nested field, using the field names as key (but with mixins field
* removed).
* <p>
* lazy evaluated. result cached.
*
* @param metadataTScope
* the translation scope for (generated) metadata classes.
* @return the bound metadata class descriptor.
*/
protected MetadataClassDescriptor bindMetadataClassDescriptor(SimplTypesScope metadataTScope)
{
MetadataClassDescriptor metadataCd = this.getMetadataClassDescriptor();
if (metadataCd == null)
{
metadataCd = metadataClassDescriptor(metadataTScope);
if (metadataCd != null)
{
this.setMetadataClassDescriptor(metadataCd); // early assignment to prevent infinite loop
this.bindMetadataFieldDescriptors(metadataTScope, metadataCd);
}
}
return metadataCd;
}
@Override
protected void customizeFieldDescriptor(SimplTypesScope metadataTScope,
MetadataFieldDescriptorProxy fdProxy)
{
super.customizeFieldDescriptor(metadataTScope, fdProxy);
MetaMetadata thisMmd = this.getTypeMmd();
if (thisMmd == null)
return; // could be collection of scalars
MetaMetadataNestedField inheritedField = (MetaMetadataNestedField) this.getSuperField();
if (inheritedField != null)
{
MetaMetadata superMmd = inheritedField.getTypeMmd();
// if (thisMmd == superMmd)
// return;
if (thisMmd == superMmd || thisMmd.isDerivedFrom(superMmd))
{
// extending type!
// MetadataClassDescriptor metadataClassDescriptor =
// thisMmd.bindMetadataClassDescriptor(metadataTScope);
MetadataClassDescriptor elementMetadataCD = thisMmd.metadataClassDescriptor(metadataTScope);
fdProxy.setElementClassDescriptor(elementMetadataCD);
}
else
{
throw new MetaMetadataException("incompatible types: " + inheritedField + " => " + this);
}
}
}
protected MetadataClassDescriptor metadataClassDescriptor(SimplTypesScope metadataTScope)
{
MetadataClassDescriptor metadataCd = this.getMetadataClassDescriptor();
if (metadataCd == null)
{
// this.inheritMetaMetadata(inheritanceHandler);
String metadataClassSimpleName = this.getMetadataClassSimpleName();
// first look up by simple name, since package names for some built-ins are wrong
metadataCd = (MetadataClassDescriptor) metadataTScope
.getClassDescriptorBySimpleName(metadataClassSimpleName);
if (metadataCd == null)
{
String metadataClassName = this.getMetadataClassName();
metadataCd = (MetadataClassDescriptor) metadataTScope
.getClassDescriptorByClassName(metadataClassName);
if (metadataCd == null)
{
try
{
Class metadataClass = Class.forName(metadataClassName);
this.setMetadataClass(metadataClass);
metadataCd = (MetadataClassDescriptor) ClassDescriptor
.getClassDescriptor(metadataClass);
metadataTScope.addTranslation(metadataClass);
}
catch (ClassNotFoundException e)
{
// e.printStackTrace();
// throw new MetaMetadataException("Cannot find metadata class: " + metadataClassName);
error("Cannot find metadata class: " + metadataClassName);
}
}
}
}
return metadataCd;
}
void findOrGenerateMetadataClassDescriptor(SimplTypesScope tscope)
{
if (mmdScopeTraversed)
return;
mmdScopeTraversed = true;
if (this.getScope() != null)
{
for (Object obj : this.getScope().values())
{
if (obj instanceof MetaMetadata)
{
MetaMetadata inlineMmd = (MetaMetadata) obj;
inlineMmd.findOrGenerateMetadataClassDescriptor(tscope);
}
}
}
if (this.getChildrenMap() != null)
for (MetaMetadataField f : this.getChildrenMap())
{
if (f instanceof MetaMetadataNestedField)
{
MetaMetadataNestedField nested = (MetaMetadataNestedField) f;
nested.findOrGenerateMetadataClassDescriptor(tscope);
}
}
}
@Override
public void addAdditionalMetaInformation(List<MetaInformation> metaInfoBuf,
MmdCompilerService compiler)
{
metaInfoBuf.add(new MetaInformation(mm_name.class, false, getName()));
if (this.getName().equals("mixins") && this.getDeclaringMmd().getName().equals("metadata"))
metaInfoBuf.add(new MetaInformation(semantics_mixin.class));
}
public void recursivelyRestoreChildComposite()
{
for (MetaMetadataField field : this.getChildrenMap())
{
if (field instanceof MetaMetadataNestedField)
((MetaMetadataNestedField) field).recursivelyRestoreChildComposite();
}
}
@Override
protected String getFingerprintString()
{
StringBuilder sb = StringBuilderUtils.acquire();
sb.append(super.getFingerprintString());
addToFp(sb, Utils.serializeToString(fieldParserElement, StringFormat.XML));
addToFp(sb, getPolymorphicScope());
addToFp(sb, getPolymorphicClasses());
addToFp(sb, getSchemaOrgItemtype());
addCollectionToFp(sb, getDefVars());
String fp = sb.toString();
StringBuilderUtils.release(sb);
return fp;
}
}