package ecologylab.bigsemantics.metadata;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import ecologylab.bigsemantics.actions.SemanticActionHandler;
import ecologylab.bigsemantics.collecting.LinkedMetadataMonitor;
import ecologylab.bigsemantics.collecting.SemanticsGlobalScope;
import ecologylab.bigsemantics.metadata.builtins.Document;
import ecologylab.bigsemantics.metadata.builtins.DocumentClosure;
import ecologylab.bigsemantics.metadata.builtins.declarations.MetadataDeclaration;
import ecologylab.bigsemantics.metadata.output.MetadataConstants;
import ecologylab.bigsemantics.metadata.scalar.MetadataString;
import ecologylab.bigsemantics.metametadata.ClassAndCollectionIterator;
import ecologylab.bigsemantics.metametadata.LinkWith;
import ecologylab.bigsemantics.metametadata.MetaMetadata;
import ecologylab.bigsemantics.metametadata.MetaMetadataCompositeField;
import ecologylab.bigsemantics.metametadata.MetaMetadataField;
import ecologylab.bigsemantics.metametadata.MetaMetadataNestedField;
import ecologylab.bigsemantics.metametadata.MetaMetadataOneLevelNestingIterator;
import ecologylab.bigsemantics.metametadata.MetaMetadataRepository;
import ecologylab.bigsemantics.metametadata.declarations.MetaMetadataFieldDeclaration;
import ecologylab.bigsemantics.model.text.CompositeTermVector;
import ecologylab.bigsemantics.model.text.ITermVector;
import ecologylab.bigsemantics.model.text.OrderedNormalizedTermVectorCache;
import ecologylab.bigsemantics.model.text.TermVectorFeature;
import ecologylab.generic.Debug;
import ecologylab.generic.HashMapArrayList;
import ecologylab.net.ParsedURL;
import ecologylab.serialization.ClassDescriptor;
import ecologylab.serialization.FieldDescriptor;
import ecologylab.serialization.FieldType;
import ecologylab.serialization.SIMPLTranslationException;
import ecologylab.serialization.ScalarUnmarshallingContext;
import ecologylab.serialization.SimplTypesScope;
import ecologylab.serialization.TranslationContext;
import ecologylab.serialization.XMLTools;
import ecologylab.serialization.annotations.simpl_scalar;
import ecologylab.serialization.deserializers.ISimplDeserializationPost;
import ecologylab.serialization.formatenums.StringFormat;
import ecologylab.serialization.library.html.Div;
import ecologylab.serialization.library.html.Input;
import ecologylab.serialization.library.html.Table;
import ecologylab.serialization.library.html.Td;
import ecologylab.serialization.library.html.Tr;
import ecologylab.serialization.serializers.ISimplSerializationPre;
/**
* This is the new metadata class that is the base class for the meta-metadata system. It contains
* all the functionality of the previous Metadata, and MetadataField classes.
*
* Classes will extend this base class to provide a nested metadata structure.
*
* @author sashikanth
*
*/
public abstract class Metadata extends MetadataDeclaration
implements MetadataBase, TermVectorFeature, Iterable<MetadataFieldDescriptor>,
ISimplSerializationPre, ISimplDeserializationPost
{
final static int INITIAL_SIZE = 5;
private static final String MIXINS_FIELD_NAME = "mixins";
/**
* Hidden reference to the MetaMetadataRepository. DO NOT access this field directly. DO NOT
* create a static public accessor. -- andruid 10/7/09.
*/
private static MetaMetadataRepository repository;
/**
* this must be a composite field. this is not the meta-metadata representing type, but the
* "local" composite field object that may carry extraction / presentation rules.
*/
private MetaMetadata metaMetadata;
/**
* this is used by the ORM module as a surrogate identifier. its value will be generated by the
* database automatically. each metadata object will have a unique one for reference. using
* surrogate ID prevents potential conflicts problems, when used with strict relational database
* systems.
*/
private long ormId;
@simpl_scalar
private float repositoryVersion;
/**
* the (composite) term vector for this field.
*/
protected CompositeTermVector termVector = null;
public static String USE_SEMANTIC_SEARCH_PREF = "use_situated_search";
/**
* the cache for ordered normalized term vectors
*/
protected OrderedNormalizedTermVectorCache orderedCompositeTermVectorCache = null;
/**
* Indicates whether or not metadata has changed since last displayed.
*/
private boolean metadataChangedForDisplay;
/**
* caching natural ID name-value pairs.
*/
private Map<String, String> cachedNaturalIdValues;
/**
* if this has been recycled.
*/
private boolean recycled;
/**
* a map from <link_with> type to linked Metadata. initial empty. no key indicates not yet tried
* download and parse. having key but null value indicates tried but failed.
*/
private Map<String, Metadata> linkedMetadata;
/**
* used for synchronization.
*/
private Object lockLinkedMetadata = new Object();
private MetadataClassDescriptor classDescriptor;
/**
* This constructor should *only* be used when marshalled Metadata is read.
*/
public Metadata()
{
super();
}
/**
* This constructor should be used by *all* live code.
*
* @param metaMetadata
*/
public Metadata(MetaMetadataCompositeField metaMetadata)
{
this();
if (metaMetadata != null)
setMetaMetadata(metaMetadata);
}
/**
* get the ormId.
*
* @return
*/
public long getOrmId()
{
return ormId;
}
/**
* set the ormId.
*
* @param ormId
*/
public void setOrmId(long ormId)
{
this.ormId = ormId;
}
public float getRepositoryVersion()
{
return repositoryVersion;
}
public void setRepositoryVersion(float repositoryVersion)
{
this.repositoryVersion = repositoryVersion;
}
/**
* Don't exclude the mixins field from Metadata.
*
* @param tagName
*
* @return true for the mixins field; otherwise false;
*/
public boolean excludeFieldByTag(String tagName)
{
return MIXINS_FIELD_NAME.equals(tagName);
}
public MetadataClassDescriptor getMetadataClassDescriptor()
{
MetadataClassDescriptor result = this.classDescriptor;
if (result == null)
{
result = (MetadataClassDescriptor) ClassDescriptor.getClassDescriptor(this);
this.classDescriptor = result;
}
return result;
}
/**
* If necessary, bind a MetaMetadata object to this. Save the result for next time.
*
* @return
*/
public MetaMetadataCompositeField getMetaMetadata()
{
// return getMetadataClassDescriptor().getMetaMetadata();
MetaMetadataCompositeField mm = metaMetadata;
if (mm == null && repository != null)
{
MetadataString metaMetadataName = getMetaMetadataNameMetadata();
MetaMetadataCompositeField bySavedName = metaMetadataName == null ? null : repository.getMMByName(metaMetadataName.getValue());
if (bySavedName != null)
mm = bySavedName;
else
{
ParsedURL location = getLocation();
MetaMetadataCompositeField byLocation = location == null ? null : isImage() ? repository.getImageMM(location) : repository.getDocumentMM(location);
// TODO by MIME type
MetaMetadataCompositeField byClass = repository.getMMByClass(getClass());
// I believe that in all the cases, we should use the class instead of the tag name, since
// the same tag name can be used for different classes! -- yin qu
// ClassDescriptor cd = classDescriptor();
// MetaMetadataCompositeField byTagName = cd == null ? null : repository.getMMByName(cd.getTagName());
// in most cases, we might use the one by location / mime type;
// but if the one by class / tag name is more specific, we might want that one (?)
if (byLocation != null)
{
if (byClass != null && byLocation.getMetadataClass().isAssignableFrom(byClass.getMetadataClass()))
mm = byClass;
else if (byClass != null && byLocation.isGenericMetadata() && !byClass.isGenericMetadata())
mm = byClass;
// else if (byTagName != null && byLocation.getMetadataClass().isAssignableFrom(byTagName.getMetadataClass()))
// mm = byTagName;
else
mm = byLocation;
}
else
mm = byClass;
}
if (mm != null)
setMetaMetadata(mm);
}
return mm;
}
/**
* get mixins.
*
* @return
*/
public List<Metadata> mixins()
{
List<Metadata> result = this.getMixins();
if (result == null)
{
result = new ArrayList<Metadata>();
this.setMixins(result);
}
return result;
}
public void addMixin(Metadata mixin)
{
if (mixin != null)
{
List<Metadata> mixins = mixins();
if (!mixins.contains(mixin))
{
HashSet<Metadata> visitedMetadata = new HashSet<Metadata>();
mixins.add(mixin);
CompositeTermVector mixinTermVector = mixin.termVector(visitedMetadata);
if (mixinTermVector != null)
termVector().add(mixinTermVector);
}
}
}
public void removeMixin(Metadata mixin)
{
if (mixin != null)
{
if (mixins().remove(mixin) && mixin.termVector != null)
termVector().remove(mixin.termVector);
}
}
/**
* @return the number of non-Null fields within this metadata
*/
public int size()
{
return numberOfVisibleFields();
}
public int numberOfVisibleFields()
{
return numberOfVisibleFields(true);
}
public int numberOfVisibleFields(boolean considerAlwaysShow)
{
int size = 0;
MetaMetadataOneLevelNestingIterator fullIterator = fullNonRecursiveMetaMetadataIterator(null);
// iterate over all fields in this & then in each mixin of this
while (fullIterator.hasNext())
{
MetaMetadataField metaMetadataField = fullIterator.next();
MetaMetadataField metaMetadata = fullIterator.currentObject(); // stays the same for until we
// iterate over all mfd's for
// it
Metadata currentMetadata = fullIterator.currentMetadata();
// When the iterator enters the metadata in the mixins "this" in getValueString has to be
// the corresponding metadata in mixin.
boolean hasVisibleNonNullField = false;
MetadataFieldDescriptor mfd = metaMetadataField.getMetadataFieldDescriptor();
// if (metaMetadata.isChildFieldDisplayed(metaMetadataField.getName()))
// {
// if (mfd.isScalar() && !mfd.isCollection())
// hasVisibleNonNullField = MetadataString.isNotNullAndEmptyValue(mfd
// .getValueString(currentMetadata));
// else if (mfd.isNested())
// {
// Metadata nestedMetadata = (Metadata) mfd.getNestedMetadata(currentMetadata);
// hasVisibleNonNullField = (nestedMetadata != null) ? (nestedMetadata
// .numberOfVisibleFields() > 0) : false;
// }
// else if (mfd.isCollection())
// {
// Collection collection = mfd.getCollection(currentMetadata);
// hasVisibleNonNullField = (collection != null) ? (collection.size() > 0) : false;
// }
// }
// "null" happens with mixins fieldAccessor b'coz getValueString() returns "null".
// TODO use MetaMetadataField.numNonDisplayedFields()
// boolean isVisibleField = !metaMetadataField.isHide()
// && ((considerAlwaysShow && metaMetadataField.isAlwaysShow()) || hasVisibleNonNullField);
if (hasVisibleNonNullField)
size++;
}
return size;
}
public void rebuildTotally()
{
if (termVector != null)
termVector.reinitialize();
rebuildCompositeTermVector();
}
/**
* Rebuilds the composite TermVector from the individual TermVectors FIXME:Not able to move to the
* MetadataBase b'coz of mixins.
*/
@Override
public void rebuildCompositeTermVector()
{
HashSet<Metadata> visitedMetadata = new HashSet<Metadata>();
// if there are no metadatafields retain the composite termvector
// because it might have meaningful entries
if (termVector == null)
{
initializeMetadataCompTermVector(visitedMetadata);
return;
}
Set<ITermVector> vectors = termVector.componentVectors();
ClassAndCollectionIterator i = metadataIterator(visitedMetadata);
while (i.hasNext())
{
MetadataBase mb = i.next();
if (mb != null)
{
// if mb is a Metadata object, this call may recursively initialize its CompositeTermVector
ITermVector mTermVector = mb.termVector(visitedMetadata);
if (mb != null && !vectors.contains(mTermVector))
termVector.add(mTermVector);
}
}
}
public void rebuildCompositeTermVector(HashSet<Metadata> visitedMetadata)
{
// if there are no metadatafields retain the composite termvector
// because it might have meaningful entries
if (termVector == null)
{
initializeMetadataCompTermVector(visitedMetadata);
return;
}
Set<ITermVector> vectors = termVector.componentVectors();
ClassAndCollectionIterator i = metadataIterator(visitedMetadata);
while (i.hasNext())
{
MetadataBase mb = i.next();
if (mb != null)
{
// if mb is a Metadata object, this call may recursively initialize its CompositeTermVector
ITermVector mTermVector = mb.termVector();
if (mb != null && !vectors.contains(mTermVector))
termVector.add(mTermVector);
}
}
}
@Override
public void serializationPreHook(TranslationContext translationContext)
{
getMetaMetadata();
}
@Override
public void deserializationPostHook(TranslationContext translationContext, Object object)
{
// if (metaMetadata != null)
// initializeMetadataCompTermVector();
}
public boolean hwSet(String tagName, String value)
{
if (setByTagName(tagName, value))
{
// value is properly set.
rebuildCompositeTermVector();
return true;
}
return false;
}
public Field getFields()
{
return null;
}
public void setMetaMetadata(MetaMetadataNestedField metaMetadata)
{
// FIXME -- get rid of all call sites for this method -- andruid 6/1/10
// see MetaMetadataSearchParser for a call site. can we avoid this call?
if (metaMetadata instanceof MetaMetadata)
this.metaMetadata = (MetaMetadata) metaMetadata;
else
this.metaMetadata = metaMetadata.getTypeMmd();
String metaMetadataName = this.metaMetadata.getName();
this.setMetaMetadataNameMetadata(new MetadataString(metaMetadataName));
}
@Override
public CompositeTermVector termVector()
{
return getOrCreateTermVector();
}
@Override
public CompositeTermVector termVector(HashSet<Metadata> visitedMetadata)
{
if (termVector == null && metaMetadata != null)
return initializeMetadataCompTermVector(visitedMetadata);
return termVector;
}
// could get called twice if termVector() is called from different threads & termVector is null.
public synchronized CompositeTermVector initializeMetadataCompTermVector(HashSet<Metadata> visitedMetadata)
{
CompositeTermVector tv = getOrCreateTermVector();
ClassAndCollectionIterator i = metadataIterator(visitedMetadata);
while (i.hasNext())
{
MetadataBase mb = i.next();
MetaMetadataField currentMMField = i.getCurrentMMField();
if (mb != null && !currentMMField.isIgnoreInTermVector()
&& !mb.ignoreInTermVector())
{
tv.add(mb.termVector(visitedMetadata));
}
}
return (termVector = tv);
}
public void clearOrderedNormalizedTermVectorCache()
{
orderedCompositeTermVectorCache = null;
}
public OrderedNormalizedTermVectorCache getOrCreateOrderedNormalizedTermVectorCache()
{
if(orderedCompositeTermVectorCache == null)
{
try
{
orderedCompositeTermVectorCache = new OrderedNormalizedTermVectorCache(termVector());
}
catch(Throwable t)
{
return new OrderedNormalizedTermVectorCache();
}
}
return orderedCompositeTermVectorCache;
}
protected CompositeTermVector getOrCreateTermVector()
{
if (termVector != null)
return termVector;
CompositeTermVector tv = new CompositeTermVector();
return tv;
}
/**
* In general, Metadata objects should contribute to the CompositeTermVector.
*/
@Override
public boolean ignoreInTermVector()
{
return false;
}
public ParsedURL getLocation()
{
return null;
}
public void hwSetLocation(ParsedURL location)
{
}
public void setLocation(ParsedURL location)
{
}
public String getContext()
{
return null;
}
/**
* Sets the value of the field context
**/
public void setContext(String context)
{
}
public void hwSetContext(String context)
{
}
public ParsedURL getNavLocation()
{
return null;
}
public void setNavLocation(ParsedURL navLocation)
{
}
public void hwSetNavLocation(ParsedURL navLocation)
{
}
// For adding mapped attributes
public void add(String key)
{
}
public Metadata get(int index)
{
return null;
}
@Override
public void recycle()
{
// TODO recycling
this.recycled = true;
}
public void recycle(HashSet<Metadata> visitedMetadata)
{
if (recycled)
return;
recycled = true;
if (metaMetadata != null)
{
MetaMetadataRepository repository = metaMetadata.getRepository();
if (repository != null)
{
LinkedMetadataMonitor monitor = repository.getLinkedMetadataMonitor();
monitor.removeMonitors(this);
}
}
ClassAndCollectionIterator iterator = metadataIterator(visitedMetadata);
for (MetadataBase metadata = iterator.next(); iterator.hasNext(); metadata = iterator.next())
{
if (metadata != null)
metadata.recycle();
}
this.recycle();
if (termVector != null)
{
// termVector can be null for those metadata created on the fly, e.g. ImageElement created
// from semantic action without metadata information (with only location & caption).
termVector.recycle();
termVector = null;
}
}
public boolean isRecycled()
{
return recycled; // (termVector != null && termVector.isRecycled());
}
public void resetRecycleStatus()
{
recycled = false;
}
public MetaMetadataOneLevelNestingIterator fullNonRecursiveMetaMetadataIterator(
MetaMetadataField metaMetadataField)
{
MetaMetadataField firstMetaMetadataField =
(metaMetadataField != null) ? metaMetadataField : metaMetadata;
return new MetaMetadataOneLevelNestingIterator(firstMetaMetadataField, this);
}
public ClassAndCollectionIterator metadataIterator(HashSet<Metadata> visitedMetadata)
{
return new ClassAndCollectionIterator(getMetaMetadata(), this, visitedMetadata);
}
public boolean hasObservers()
{
return termVector != null && termVector.countObservers() > 0;
}
public boolean hasTermVector()
{
return termVector != null;
}
public static void setRepository(MetaMetadataRepository repo)
{
repository = repo;
}
// FIXEME:The method has to search even all the mixins for the key.
public MetadataFieldDescriptor getFieldDescriptorByTagName(String tagName)
{
return getMetadataClassDescriptor().getFieldDescriptorByTag(tagName,
repository.metadataTranslationScope());
}
/**
* Sets the field to the specified value and wont rebuild composteTermVector
*
* @param fieldName
* @param value
*/
// TODO -- May throw exception if there is no field accessor.
// FIXME -- resolve with MetadataBase
// public boolean setByTagName(String tagName, String value)
// {
// tagName = tagName.toLowerCase();
// // Taking care of mixins
// Metadata metadata = getMetadataWhichContainsField(tagName);
//
// if (value != null && value.length() != 0)
// {
// if (metadata != null)
// {
// FieldDescriptor fieldAccessor = getFieldDescriptorByTagName(tagName);
// if (fieldAccessor != null && value != null && value.length() != 0)
// {
// fieldAccessor.set(metadata, value);
// return true;
// }
// else
// {
// debug("Not Able to set the field: " + tagName);
// return false;
// }
// }
// }
// return false;
// }
public boolean setByTagName(String tagName, String value)
{
return setByTagName(tagName, value, null);
}
/**
* Unmarshall the valueString and set the field to
*
* @param tagName
* @param marshalledValue
* @param scalarUnMarshallingContext
* @return
*/
public boolean setByTagName(String tagName, String marshalledValue,
ScalarUnmarshallingContext scalarUnMarshallingContext)
{
// FIXME -- why is this necessary???????????????????????
if (marshalledValue != null && marshalledValue.length() != 0)
{
// tagName = tagName.toLowerCase(); // FIXME -- get rid of this!
MetadataFieldDescriptor fieldDescriptor = getFieldDescriptorByTagName(tagName);
if (fieldDescriptor != null /* && value != null && value.length()!=0 */) // allow set to
// nothing --
// andruid & andrew
// 4/14/09
{
// FIXME -- override this method in MetadataFieldDescriptor!!!
fieldDescriptor.set(this, marshalledValue, scalarUnMarshallingContext);
return true;
}
else
{
Debug.debugT(this, "Not Able to set the field: " + tagName);
}
}
return false;
}
public boolean setByFieldName(String fieldName, String marshalledValue)
{
return setByFieldName(fieldName, marshalledValue, null);
}
public boolean setByFieldName(String fieldName, String marshalledValue, ScalarUnmarshallingContext scalarUnmarshallingContext)
{
if (marshalledValue != null && marshalledValue.length() != 0)
{
MetadataFieldDescriptor fieldDescriptor = getFieldDescriptorsByFieldName().get(fieldName);
if (fieldDescriptor != null)
{
fieldDescriptor.set(this, marshalledValue, scalarUnmarshallingContext);
return true;
}
else
{
Debug.warning(this, "Cannot find field [" + fieldName + "] on " + this);
}
}
return false;
}
@Override
public boolean hasCompositeTermVector()
{
return termVector != null;
}
@Override
public Iterator<MetadataFieldDescriptor> iterator()
{
return getMetadataClassDescriptor().iterator();
}
/**
* @return
*/
public HashMapArrayList<String, MetadataFieldDescriptor> getFieldDescriptorsByFieldName()
{
return getMetadataClassDescriptor().getFieldDescriptorsByFieldName();
}
/**
* Convenience method for type checking related to Image-ness.
* Base implementation:
*
* @return false
*/
public boolean isImage()
{
return false;
}
/**
* Convenience method for type checking related to InfoComposition-ness
* Base implementation
* @return false
*/
public boolean isInformationComposition()
{
return false;
}
/**
* Convenience method for type checking related to CompoundDocument-ness.
* Base implementation:
*
* @return false
*/
public boolean isRichDocument()
{
return false;
}
public boolean isGui()
{
return false;
}
public void serializeToHtml(Appendable a, TranslationContext serializationContext)
throws IllegalArgumentException, IllegalAccessException, IOException, SIMPLTranslationException
{
Table htmlTable = new Table();
renderHtml(htmlTable, serializationContext, false, true, false);
SimplTypesScope.serialize(htmlTable, a, StringFormat.XML);
}
public Table addLabelAndReturnTable(Tr outputRow, int numElements, Metadata metadata, MetaMetadataCompositeField mmCompositeField)
{
//Tr tr = new Tr();
Td labelTd = new Td();
Div div = new Div();
Input button = new Input(); //expanding button
div.setCssClass(MetadataConstants.METADATA_TEXT);//css for all
///div.setText(metadata.getMetaMetadata().getDisplayedLabel());//label for all
div.setText(mmCompositeField.getLabel());
if (numElements > 1)//add button
{
labelTd.setCssClass(MetadataConstants.FIELD_NAME);
button.setType(MetadataConstants.IMAGE);
button.setCssClass(MetadataConstants.COMPOSITE);
button.setSrc(MetadataConstants.IMAGE_URL);
button.setValue("");
div.members.add(button);
}
labelTd.items.add(div);
outputRow.cells.add(labelTd);
Table placeHolderTable = new Table();
return placeHolderTable;
}
public void renderHtml(Table htmlTable, TranslationContext serializationContext, boolean recursing,
boolean encapsulateInTable, boolean hideNextCompositLabel) throws IllegalArgumentException,
IllegalAccessException, IOException, SIMPLTranslationException
{
renderHtml(htmlTable, serializationContext, recursing, encapsulateInTable,
hideNextCompositLabel, new HashSet());
}
public void renderHtml(Table htmlTable, TranslationContext serializationContext, boolean recursing,
boolean encapsulateInTable, boolean hideNextCompositLabel, Set bookKeeper) throws IllegalArgumentException,
IllegalAccessException, IOException, SIMPLTranslationException
{
// TODO currently, when there is a back reference, we render nothing to the HTML
// but what we actually need is to render a title or part of the metadata with a hyperlink to
// the real data.
if (bookKeeper.contains(this))
return;
bookKeeper.add(this);
//System.out.println(" debug report 1: metadata name form top is "+this.getMetaMetadataName());
MetadataClassDescriptor classDescriptor = this.getMetadataClassDescriptor();
MetaMetadataOneLevelNestingIterator fullIterator = fullNonRecursiveMetaMetadataIterator(null);
int numElements = numberOfVisibleFields(false);
boolean hasXmlText = classDescriptor.hasScalarFD();
if (numElements > 0 || hasXmlText)
{
Tr tr = new Tr();
Table compositeTable = new Table();
Td nestedTd = new Td();
String schemaOrgItemtype = null;
MetaMetadataCompositeField compositeMmd = this.getMetaMetadata();
if (compositeMmd instanceof MetaMetadata)
schemaOrgItemtype = compositeMmd.getSchemaOrgItemtype();
else
schemaOrgItemtype = compositeMmd.getTypeMmd().getSchemaOrgItemtype();
if (schemaOrgItemtype != null)
htmlTable.setSchemaOrgItemType(schemaOrgItemtype);
while (fullIterator.hasNext())
{
MetaMetadataField mmdField = fullIterator.next();
final Metadata currentMetadata = fullIterator.currentMetadata();
MetadataFieldDescriptor childFD = mmdField.getMetadataFieldDescriptor();
FieldDescriptor navigatesFD = this.getFieldDescriptorByTagName(mmdField.getNavigatesTo());
if (!mmdField.isHide())
{
FieldType type = childFD.getType();
String textCssClass = mmdField.getStyleName();
if (MetadataConstants.DEFAULT.equals(textCssClass))
textCssClass = MetadataConstants.METADATA_TEXT;
if (type == FieldType.SCALAR)
{
if (!childFD.getScalarType().isDefaultValue(childFD.getField(), currentMetadata))
{
Tr scalarTr = new Tr();
scalarTr.setId("mmd_" + childFD.getTagName());
String tagName = childFD.getTagName();
boolean hasNavigatesTo = navigatesFD != null;
hasNavigatesTo = hasNavigatesTo && !navigatesFD.isDefaultValue(currentMetadata);
if (!hasNavigatesTo && (tagName.equals(MetadataConstants.LOCATION) || tagName.equals(MetadataConstants.LINK)))
{
navigatesFD = childFD;
}
childFD.appendHtmlValueAsAttribute(currentMetadata, serializationContext, scalarTr,
mmdField.getLabel(), MetadataConstants.FIELD_NAME, textCssClass, navigatesFD, mmdField.getSchemaOrgItemprop());
if (recursing)
compositeTable.rows.add(scalarTr);
htmlTable.rows.add(scalarTr);
}
}
else
{
Object thatReferenceObject = null;
Field childField = childFD.getField();
thatReferenceObject = childField.get(this);
if (thatReferenceObject == null)
continue;
// final boolean isScalar = (type == COLLECTION_SCALAR || type == MAP_SCALAR);
Collection thatCollection;
switch (type)
{
case COLLECTION_ELEMENT:
case COLLECTION_SCALAR:
case MAP_ELEMENT:
case MAP_SCALAR:
thatCollection = XMLTools.getCollection(thatReferenceObject);
break;
default:
thatCollection = null;
break;
}
if ((thatCollection != null) && (thatCollection.size() != 0))
{
int i = 0;
Tr nestedTr = new Tr();
nestedTr.setCssClass(MetadataConstants.NESTED);
if (childFD.isWrapped())
childFD.writeHtmlWrap(false, thatCollection.size(), mmdField.getLabel(), nestedTr);
Td collectionTd = new Td();
collectionTd.setCssClass(MetadataConstants.NESTED_VALUE);
nestedTr.cells.add(collectionTd);
for (Object next : thatCollection)
{
if (next instanceof Metadata)
{
//System.out.println(" debug report 4: within itterator 1 "+((Metadata)next).getMetaMetadataName());
Table nestedTable = new Table();
Metadata collectionSubElementState = (Metadata) next;
collectionSubElementState.renderHtml(nestedTable, serializationContext, true, true, false, bookKeeper); // This collection may add a composite element....
//remove last row of this table because it is empty
if(nestedTable.rows.size() > 1)
{
nestedTable.rows.remove(nestedTable.rows.size() - 1);
}
collectionTd.items.add(nestedTable);
}
}
if (childFD.isWrapped())
nestedTr.cells.add(collectionTd);
htmlTable.rows.add(nestedTr);
}
else if (thatReferenceObject instanceof Metadata) // type is COMPOSITE_ELEMENT
{
Tr compositeTr = new Tr();
Td compositeTd = new Td();
tr.setCssClass(MetadataConstants.NESTED);
Metadata nestedMD = (Metadata) thatReferenceObject;
FieldDescriptor compositeAsScalarFD = ClassDescriptor.getClassDescriptor(thatReferenceObject).getScalarValueFieldDescripotor();
if (compositeAsScalarFD != null)
{
childFD.writeCompositeHtmlWrap(false, mmdField.getLabel(), mmdField.getSchemaOrgItemtype(), compositeTr);
}
else
{
int fieldsShownInHtml = ((Metadata)thatReferenceObject).numberOfVisibleFields(false);
if(((Metadata)thatReferenceObject).hasLocation())
fieldsShownInHtml -= 1;//not shown in HTML
Table nestedTable = addLabelAndReturnTable(compositeTr, fieldsShownInHtml, (Metadata)thatReferenceObject, (MetaMetadataCompositeField) mmdField);
nestedMD.renderHtml(nestedTable, serializationContext, true, false, true, bookKeeper); //also called with a scholarly article...
compositeTd.items.add(nestedTable);
}
if(compositeTd.items.size() > 0 && compositeTd.items.get(0) instanceof Table)
{
Table innerTable = (Table) compositeTd.items.get(0);
//remove last row from this table because it is empty
if(innerTable.rows.size() > 1)
innerTable.rows.remove(innerTable.rows.size()-1);
}
compositeTr.cells.add(compositeTd);
htmlTable.rows.add(compositeTr);
}
}
}
}
if (recursing && numElements > 1)
{
compositeTable.rows.add(tr);
nestedTd.items.add(compositeTable);
tr.cells.add(nestedTd);
}
htmlTable.rows.add(tr);
}
}
public boolean hasMetadataChanged()
{
return metadataChangedForDisplay;
}
public void setMetadataChanged(boolean value)
{
Debug.println(this, "setMetadataChanged()");
this.metadataChangedForDisplay = value;
}
/**
* Iterator over fields to find all metadata fields that have changed.
*
* @return Collection of fields that have changed since last displayed.
*/
public ArrayList<Metadata> findChangedMetadataFields()
{
ArrayList<Metadata> result = new ArrayList<Metadata>();
ClassAndCollectionIterator iterator = this.metadataIterator(null);
while (iterator.hasNext())
{
MetadataBase fieldValue = iterator.next();
if (fieldValue instanceof Metadata)
{
final Metadata metadata = (Metadata) fieldValue;
if (metadata.hasMetadataChanged())
result.add(metadata);
result.addAll(metadata.findChangedMetadataFields());
}
}
return result;
}
/**
* Lookup the value of a metadata field specified by "." delimited name used to navigate the
* meta-metadata tree.
*
* @param fieldPath
* "." delimited path to metadata field
* @return
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public MetadataBase lookupMetadataValue(String fieldPath) throws IllegalArgumentException,
IllegalAccessException
{
String[] split = fieldPath.split("\\.", 2);
MetaMetadataField mmField = metaMetadata.lookupChild(split[0]);
MetadataBase value = (MetadataBase) mmField.getMetadataFieldDescriptor().getField().get(this);
if (split.length > 1)
value = ((Metadata) value).lookupMetadataValue(split[1]);
return value;
}
public String getNaturalIdValue(String naturalId)
{
if (cachedNaturalIdValues == null)
{
cachedNaturalIdValues = new HashMap<String, String>();
}
if (cachedNaturalIdValues.containsKey(naturalId))
return cachedNaturalIdValues.get(naturalId);
else
{
String value = getNaturalIdValueHelper(naturalId);
cachedNaturalIdValues.put(naturalId, value);
return value;
}
}
private String getNaturalIdValueHelper(String naturalId)
{
MetaMetadataCompositeField mmcf = getMetaMetadata();
if (mmcf == null)
return null;
MetaMetadata mmd;
if (mmcf instanceof MetaMetadata)
{
mmd = (MetaMetadata) mmcf;
}
else
{
String mmdName = mmcf.getTypeName();
mmd = repository.getMMByName(mmdName);
}
MetaMetadataField field = mmd.getNaturalIdField(naturalId);
MetadataFieldDescriptor fd = field.getMetadataFieldDescriptor();
String valueString = fd.getValueString(this);
String format = field.getFormat();
if (format != null)
{
// built-in support for text format
if ("text".equals(format))
{
valueString = valueString.trim().replaceAll("[^a-zA-Z0-9]", " ").replaceAll("\\s+", " ").toLowerCase();
}
}
return valueString;
}
private Map<String, Metadata> getLinkedMetadata()
{
if (linkedMetadata == null)
{
synchronized (lockLinkedMetadata)
{
if (linkedMetadata == null)
{
Map<String, Metadata> linkedMetadata = new HashMap<String, Metadata>();
if (getLinkedMetadataList() != null)
{
for (Metadata linkedMd : getLinkedMetadataList())
{
linkedMetadata.put(linkedMd.getMetaMetadata().getName(), linkedMd);
}
}
this.linkedMetadata = linkedMetadata;
}
}
}
return linkedMetadata;
}
public Set<String> getLinkedMetadataKeys()
{
synchronized (lockLinkedMetadata)
{
return getLinkedMetadata().keySet();
}
}
public Metadata getLinkedMetadata(String name)
{
synchronized (lockLinkedMetadata)
{
return getLinkedMetadata().get(name);
}
}
public void addLinkedMetadata(LinkWith lw, Metadata metadata)
{
synchronized (lockLinkedMetadata)
{
getLinkedMetadata().put(lw.key(), metadata);
if (getLinkedMetadataList() == null)
setLinkedMetadataList(new ArrayList<Metadata>());
getLinkedMetadataList().add(metadata);
}
}
public SemanticActionHandler pendingSemanticActionHandler;
/**
* Determine if this already has a mixin assignable from the class passed in.
*
* @param mixinClass
* @return
*/
public boolean containsMixin(Class<? extends Metadata> mixinClass)
{
if (getMixins() == null || mixinClass == null)
return false;
for (Metadata mixin: getMixins())
{
if (mixinClass.isAssignableFrom(mixin.getClass()))
return true;
}
return false;
}
/**
* Determine if this already has a mixin assignable from the class passed in.
*
* @param mixinName The name of the meta-metadata for the mixin you seek to match.
*
* @return
*/
public boolean containsMixin(String mixinName)
{
if (getMixins() == null || mixinName == null)
return false;
for (Metadata mixin: getMixins())
{
if (mixinName.equals(mixin.getMetaMetadata().getName()))
return true;
}
return false;
}
public<MI extends Metadata> MI getMixin(Class<MI> mixinClass)
{
if (getMixins() == null || mixinClass == null)
return null;
for (Metadata mixin: getMixins())
{
if (mixinClass.isAssignableFrom(mixin.getClass()))
return (MI) mixin;
}
return null;
}
public boolean hasLocation()
{
return false;
}
public boolean isClipping()
{
return false;
}
/**
* Base class method for overriding. Does nothing.
* @param semanticsSessionScope
*/
public void setSemanticsSessionScope(SemanticsGlobalScope semanticsSessionScope)
{
// FIXME ??? who did this? please add comment on what's happening here
}
/**
* dest should be of the same type or a subtype of src.
*
* @param dest
* @param src
*/
public static void fieldWiseCopy(Metadata dest, Metadata src)
{
if (src != null && dest != null && src.getClass().isAssignableFrom(dest.getClass()))
{
Map<String, MetadataFieldDescriptor> fields = src.getFieldDescriptorsByFieldName();
for (String fieldName : fields.keySet())
{
MetadataFieldDescriptor fd = fields.get(fieldName);
Object value = fd.getValue(src);
if (value != null)
{
MetadataFieldDescriptor destFd = dest.getFieldDescriptorsByFieldName().get(fieldName);
if (destFd != null)
{
destFd.setField(dest, value);
}
}
}
if (dest instanceof Document && src instanceof Document)
{
Document srcDoc = (Document) src;
Document destDoc = (Document) dest;
DocumentClosure downloadClosure = srcDoc.getOrConstructClosure();
downloadClosure.changeDocument(destDoc);
}
}
}
}