/** * */ package ecologylab.bigsemantics.metametadata; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import ecologylab.bigsemantics.actions.SemanticAction; import ecologylab.bigsemantics.actions.SemanticActionTranslationScope; import ecologylab.bigsemantics.collecting.LinkedMetadataMonitor; import ecologylab.bigsemantics.html.utils.StringBuilderUtils; import ecologylab.bigsemantics.metadata.Metadata; import ecologylab.bigsemantics.metadata.MetadataClassDescriptor; import ecologylab.bigsemantics.metadata.MetadataFieldDescriptor; import ecologylab.generic.ReflectionTools; import ecologylab.net.ParsedURL; import ecologylab.serialization.SimplTypesScope; import ecologylab.serialization.XMLTools; import ecologylab.serialization.annotations.simpl_collection; import ecologylab.serialization.annotations.simpl_composite; import ecologylab.serialization.annotations.simpl_inherit; import ecologylab.serialization.annotations.simpl_map; import ecologylab.serialization.annotations.simpl_nowrap; import ecologylab.serialization.annotations.simpl_other_tags; import ecologylab.serialization.annotations.simpl_scalar; import ecologylab.serialization.annotations.simpl_scope; import ecologylab.serialization.annotations.simpl_tag; import ecologylab.serialization.types.element.IMappable; /** * The meta-metadata class. * * @author quyin * @author damaraju */ @SuppressWarnings({ "rawtypes", "unchecked" }) @simpl_inherit public class MetaMetadata extends MetaMetadataCompositeField implements IMappable<String>// , HasLocalTranslationScope { public enum Visibility { GLOBAL, PACKAGE, } @simpl_collection("selector") @simpl_nowrap @mm_dont_inherit private ArrayList<MetaMetadataSelector> selectors; @simpl_collection("example_url") @simpl_nowrap @mm_dont_inherit private ArrayList<ExampleUrl> exampleUrls; /** * Filter location <b>before</b> downloading the page. */ @simpl_composite @mm_dont_inherit private FilterLocation rewriteLocation; /** * Filter location <b>after</b> downloading the page. */ @simpl_composite @mm_dont_inherit private FilterLocation filterLocation; @simpl_scalar private String parser; @simpl_scalar private String searchResultIterator; @simpl_scalar private String finalPageCanary; @simpl_scalar private String resultsPerPage; @simpl_scalar @mm_dont_inherit private String forceLocationFilter; @simpl_scalar @mm_dont_inherit private String extractWith; @simpl_collection @simpl_scope(SemanticActionTranslationScope.SEMANTIC_ACTION_TRANSLATION_SCOPE) @mm_dont_inherit private ArrayList<SemanticAction> beforeSemanticActions; @simpl_collection @simpl_tag("operations") @simpl_other_tags({ "semantic_actions" }) @simpl_scope(SemanticActionTranslationScope.SEMANTIC_ACTION_TRANSLATION_SCOPE) private ArrayList<SemanticAction> semanticActions; @simpl_collection @simpl_scope(SemanticActionTranslationScope.SEMANTIC_ACTION_TRANSLATION_SCOPE) @mm_dont_inherit private ArrayList<SemanticAction> afterSemanticActions; @simpl_scalar @mm_dont_inherit private boolean builtIn; @simpl_scalar private RedirectHandling redirectHandling; @simpl_scalar private String bibtexType; /** * Mixins are needed so that we can have objects of multiple metadata classes in side a single * metadata class. It basically provide us to simulate the functionality of multiple inheritance * which is missing in java. */ @simpl_collection("mixins") @simpl_nowrap private ArrayList<String> mixins; @simpl_scalar private String collectionOf; @simpl_collection("url_generator") @simpl_nowrap @mm_dont_inherit private ArrayList<UrlGenerator> urlGenerators; private Map<String, MetaMetadataField> naturalIds; @simpl_map("link_with") @simpl_nowrap private HashMap<String, LinkWith> linkWiths; @simpl_scalar protected Visibility visibility = Visibility.GLOBAL; @simpl_scalar private String renderer; @simpl_scalar private boolean noCache; @simpl_scalar @mm_dont_inherit private String cacheLife; private long cacheLifeMs = -1; private Map<MetaMetadataSelector, MetaMetadata> reselectMap; SimplTypesScope localMetadataTranslationScope; public MetaMetadata() { super(); } protected MetaMetadata(MetaMetadataField copy, String name) { super(copy, name); } public ArrayList<MetaMetadataSelector> getSelectors() { if (selectors == null) return MetaMetadataSelector.NULL_SELECTOR; return selectors; } public ArrayList<ExampleUrl> getExampleUrls() { return exampleUrls; } public FilterLocation getRewriteLocation() { return rewriteLocation == null ? filterLocation : rewriteLocation; } public FilterLocation getFilterLocation() { return filterLocation; } public String getParser() { return parser; } public ArrayList<SemanticAction> getBeforeSemanticActions() { return beforeSemanticActions; } /** * @return the semanticActions */ public ArrayList<SemanticAction> getSemanticActions() { return semanticActions; } public ArrayList<SemanticAction> getAfterSemanticActions() { return afterSemanticActions; } /** * @return the collectionOf */ public String getCollectionOf() { return collectionOf; } /** * @return the redirectHandling */ public RedirectHandling getRedirectHandling() { return redirectHandling; } public Map<String, MetaMetadataField> getNaturalIdFields() { return naturalIds; } public MetaMetadataField getNaturalIdField(String fieldName) { return naturalIds == null ? null : naturalIds.get(fieldName); } public Map<String, LinkWith> getLinkWiths() { return linkWiths; } public long getCacheLifeMs() { if (cacheLifeMs >= 0) { return cacheLifeMs; } cacheLifeMs = getCacheLifeMsHelper(cacheLife); if (cacheLifeMs < 0) { cacheLifeMs = getCacheLifeMsHelper(getRepository().getDefaultCacheLife()); } if (cacheLifeMs < 0) { cacheLifeMs = 0; } return cacheLifeMs; } private long getCacheLifeMsHelper(String cacheLifeSpec) { if (cacheLifeSpec != null) { long num = -1; String unit = null; // parse cacheLife int i = 0; while (i < cacheLifeSpec.length() && Character.isDigit(cacheLifeSpec.charAt(i))) { ++i; } if (i > 0 && i < cacheLifeSpec.length()) { num = Long.parseLong(cacheLifeSpec.substring(0, i)); unit = cacheLifeSpec.substring(i); } if (num > 0 && unit != null) { if (unit.equals("ms")) { return num; } if (unit.equals("s")) { return num * 1000; } if (unit.equals("m")) { return num * 1000 * 60; } if (unit.equals("h")) { return num * 1000 * 60 * 60; } if (unit.equals("d")) { return num * 1000 * 60 * 60 * 24; } } } return -1; } public Map<MetaMetadataSelector, MetaMetadata> getReselectMap() { return reselectMap; } public SimplTypesScope getLocalMetadataTypesScope() { return this.localMetadataTranslationScope; } SimplTypesScope localMetadataTypesScope(SimplTypesScope metadataTScope) { return localMetadataTranslationScope != null ? localMetadataTranslationScope : SimplTypesScope .get("mmd_local_tscope:" + this.getName(), new SimplTypesScope[] { metadataTScope }); } /** * This always returns null since there is no "field" that this mmd inherits from. */ @Override public MetaMetadataField getSuperField() { return null; } public MetaMetadata getSuperMmd() { return (MetaMetadata) super.getSuperField(); } public void setSuperMmd(MetaMetadata result) { setSuperField(result); } @Override protected boolean isInlineDefinition() { return false; } @Override public boolean isBuiltIn() { return builtIn; } public boolean isRootMetaMetadata() { return isRootMetaMetadata(this); } public boolean isNoCache() { return noCache; } public boolean isDerivedFrom(MetaMetadata base) { MetaMetadata mmd = this; while (mmd != null) { if (mmd == base) return true; mmd = mmd.getSuperMmd(); } return false; } public void addSelector(MetaMetadataSelector s) { if (selectors == null) selectors = new ArrayList<MetaMetadataSelector>(); selectors.add(s); } /** * @return the mimeTypes */ public ArrayList<String> getMimeTypes() { ArrayList<String> result = null; for (MetaMetadataSelector selector : getSelectors()) { if (result == null) result = new ArrayList<String>(); ArrayList<String> mimeTypes = selector.getMimeTypes(); if (mimeTypes != null) result.addAll(mimeTypes); } return result; } public ArrayList<String> getSuffixes() { ArrayList<String> result = null; for (MetaMetadataSelector selector : getSelectors()) { if (result == null) result = new ArrayList<String>(); ArrayList<String> suffixes = selector.getSuffixes(); if (suffixes != null) result.addAll(suffixes); } return result; } public void addNaturalIdField(String naturalId, MetaMetadataField childField) { if (naturalIds == null) { naturalIds = new HashMap<String, MetaMetadataField>(); } naturalIds.put(naturalId, childField); } public void addLinkWith(LinkWith lw) { if (linkWiths == null) { linkWiths = new HashMap<String, LinkWith>(); } linkWiths.put(lw.key(), lw); } public void addReselectEntry(MetaMetadataSelector selector, MetaMetadata mmd) { if (reselectMap == null) { reselectMap = new HashMap<MetaMetadataSelector, MetaMetadata>(); } reselectMap.put(selector, mmd); } public static boolean isRootMetaMetadata(MetaMetadata mmd) { return mmd.getName().equals(ROOT_MMD_NAME); } @Override public String key() { return getName(); } @Override public String getTypeName() { if (getType() != null) return getType(); return getName(); } /** * @return for meta-metadata defining a new mmd type, return the super meta-metadata type name * (extends= or "metadata"); for meta-metadata decorating an existent mmd type, return the * decorated meta-metadata type name (type=). */ public String getSuperMmdTypeName() { // decorative if (getType() != null) return getType(); // definitive if (getExtendsAttribute() != null) return getExtendsAttribute(); else return "metadata"; } @Override protected String getMetadataClassName() { return this.packageName() + "." + this.getMetadataClassSimpleName(); } /** * * @return the corresponding Metadata class simple name. */ @Override protected String getMetadataClassSimpleName() { if (this.isBuiltIn() || this.isNewMetadataClass()) { // new definition return XMLTools.classNameFromElementName(this.getName()); } else { // re-using existing type // do not use this.type directly because we don't know if that is a definition or just // re-using exsiting type MetaMetadata inheritedMmd = this.getSuperMmd(); return inheritedMmd == null ? null : inheritedMmd.getMetadataClassSimpleName(); } } public Metadata constructMetadata() { return constructMetadata(this.getRepository().metadataTranslationScope()); } /** * Lookup the Metadata class that corresponds to the (tag) name of this, using the * DefaultMetadataTranslationSpace. Assuming that is found, use reflection to instantiate it. * * @return An instance of the Metadata subclass that corresponds to this, or null, if there is * none. */ public Metadata constructMetadata(SimplTypesScope ts) { Metadata result = null; Class<? extends Metadata> metadataClass = (Class<? extends Metadata>) this.getMetadataClassDescriptor().getDescribedClass(); if (metadataClass != null) { Class[] argClasses = new Class[] { MetaMetadataCompositeField.class }; Object[] argObjects = new Object[] { this }; result = ReflectionTools.getInstance(metadataClass, argClasses, argObjects); if (mixins != null && mixins.size() > 0) { for (String mixinName : mixins) { MetaMetadata mixinMM = getRepository().getMMByName(mixinName); if (mixinMM != null) { Metadata mixinMetadata = mixinMM.constructMetadata(ts); if (mixinMetadata != null) result.addMixin(mixinMetadata); } } } result.setMetaMetadataName(getName()); result.setMetaMetadata(this); } return result; } public ParsedURL generateUrl(String naturalId, String value) { if (urlGenerators != null) { for (UrlGenerator ug : urlGenerators) { if (ug.canGenerate(naturalId)) { return ug.generate(getRepository(), naturalId, value); } } } return null; } void setUpLinkWith(MetaMetadataRepository repository) { LinkedMetadataMonitor monitor = repository.getLinkedMetadataMonitor(); if (linkWiths != null) { for (String lwName : linkWiths.keySet()) { LinkWith lw = linkWiths.get(lwName); MetaMetadata targetMmd = this.getRepository().getMMByName(lw.getName()); if (targetMmd != null) { monitor.registerName(lw.getName()); String name = this.getName(); monitor.registerName(name); if (targetMmd.getLinkWiths() != null && targetMmd.getLinkWiths().containsKey(name)) { // if there is already a reverse link, just make sure the reverse link reference is set LinkWith r = targetMmd.getLinkWiths().get(name); if (!r.isReverse()) { // warning("not encouraging explicitly defining reverse links!"); r.setReverse(true); lw.setReverseLink(r); } } else { // if there isn't, create a new one LinkWith r = lw.createReverseLink(name); targetMmd.addLinkWith(r); } } else { error("link_with: meta-metadata not found: " + lw.getName()); } } } } protected void inheritSemanticActions(MetaMetadata inheritedMmd) { if (semanticActions == null) { semanticActions = inheritedMmd.getSemanticActions(); } if (afterSemanticActions == null) { afterSemanticActions = inheritedMmd.getAfterSemanticActions(); } } @Override public MetadataClassDescriptor bindMetadataClassDescriptor(SimplTypesScope metadataTScope) { if (this.getMetadataClassDescriptor() != null) return this.getMetadataClassDescriptor(); // create a temporary local metadata translation scope SimplTypesScope localMetadataTScope = localMetadataTypesScope(metadataTScope); // record the initial number of classes in the local translation scope int initialLocalTScopeSize = localMetadataTScope.entriesByClassName().size(); // do actual stuff ... super.bindMetadataClassDescriptor(localMetadataTScope); // if tag overlaps, or there are fields using classes not in metadataTScope, use localTScope MetadataClassDescriptor thisCd = this.getMetadataClassDescriptor(); if (thisCd != null) { thisCd.setDefiningMmdIfNotSet(this); MetadataClassDescriptor thatCd = (MetadataClassDescriptor) metadataTScope .getClassDescriptorByTag(thisCd.getTagName()); if (thisCd != thatCd) { localMetadataTScope.addTranslation(thisCd); this.localMetadataTranslationScope = localMetadataTScope; } else if (localMetadataTScope.entriesByClassName().size() > initialLocalTScopeSize) this.localMetadataTranslationScope = localMetadataTScope; else this.localMetadataTranslationScope = metadataTScope; // we should have stuffs in the scope already thisCd.resolvePolymorphicAnnotations(); thisCd.resolveUnresolvedClassesAnnotationFDs(); thisCd.resolveUnresolvedScopeAnnotationFDs(); } // return the bound metadata class descriptor return thisCd; } @Override void findOrGenerateMetadataClassDescriptor(SimplTypesScope tscope) { if (this.getMetadataClassDescriptor() == null) { // this.inheritMetaMetadata(inheritanceHandler); MetaMetadata superMmd = this.getSuperMmd(); if (superMmd != null) { superMmd.findOrGenerateMetadataClassDescriptor(tscope); } if (this.getMetadataClassDescriptor() == null) { // here we need to do another check, because superMmd.findOrGenerateMetadataClassDescriptor() // can be recursive and already set the class descriptor for this. MetadataClassDescriptor superCd = superMmd == null ? null : superMmd.getMetadataClassDescriptor(); if (this.isNewMetadataClass()) { String tagOrName = this.getTagOrName(); String className = XMLTools.classNameFromElementName(this.getName()); String packageName = this.packageName(); MetadataClassDescriptor cd = new MetadataClassDescriptor(this, tagOrName, this.getComment(), packageName, className, superCd, null); // setting this early allows referring to the same class in fields this.setMetadataClassDescriptor(cd); for (MetaMetadataField f : this.getChildrenMap()) { if (f.getDeclaringMmd() == this && f.getSuperField() == null) { MetadataFieldDescriptor fd = f.findOrGenerateMetadataFieldDescriptor(tscope, cd); cd.addMetadataFieldDescriptor(fd); } // if (!f.isCloned() && f instanceof MetaMetadataNestedField) if (f.parent() == this && f instanceof MetaMetadataNestedField) { ((MetaMetadataNestedField) f).findOrGenerateMetadataClassDescriptor(tscope); } } MetadataClassDescriptor existingCdWithThisTag = (MetadataClassDescriptor) tscope .getClassDescriptorByTag(tagOrName); if (existingCdWithThisTag != null) { warning("Class descriptor exists for tag [" + tagOrName + "]: " + existingCdWithThisTag); } tscope.addTranslation(cd); } else { this.setMetadataClassDescriptor(superCd); } if (this.getScope() != null) { for (Object obj : this.getScope().values()) { if (obj instanceof MetaMetadata) { MetaMetadata inlineMmd = (MetaMetadata) obj; inlineMmd.findOrGenerateMetadataClassDescriptor(tscope); } } } } } } @Override protected String getFingerprintString() { StringBuilder sb = StringBuilderUtils.acquire(); sb.append(super.getFingerprintString()); addToFp(sb, parser); addToFp(sb, getExtendsAttribute()); addCollectionToFp(sb, mixins); String fp = sb.toString(); StringBuilderUtils.release(sb); return fp; } }