package ecologylab.bigsemantics.metametadata;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ecologylab.bigsemantics.html.utils.StringBuilderUtils;
import ecologylab.bigsemantics.metadata.MetadataClassDescriptor;
import ecologylab.bigsemantics.metadata.MetadataFieldDescriptor;
import ecologylab.bigsemantics.metametadata.declarations.MetaMetadataCompositeFieldDeclaration;
import ecologylab.generic.HashMapArrayList;
import ecologylab.serialization.SimplTypesScope;
import ecologylab.serialization.XMLTools;
import ecologylab.serialization.annotations.simpl_inherit;
import ecologylab.serialization.annotations.simpl_tag;

/**
 * A composite field.
 * 
 * @author quyin
 */
@SuppressWarnings(
{ "rawtypes", "unchecked" })
@simpl_inherit
@simpl_tag("composite")
public class MetaMetadataCompositeField extends MetaMetadataCompositeFieldDeclaration
    implements MMDConstants
{

  static interface AttributeChangeListener
  {
    void typeChanged(String newType);

    void extendsChanged(String newExtends);

    void tagChanged(String newTag);

    void inlineMmdChanged(MetaMetadata newInlineMmd);

    void typeMmdChanged(MetaMetadata newTypeMmd);

    void scopeChanged(MmdScope newScope);
  }

  static Logger           logger;

  static
  {
    logger = LoggerFactory.getLogger(MetaMetadataCompositeField.class);
  }

  private MMSelectorType  mmSelectorType          = MMSelectorType.DEFAULT;

  /**
   * for caching getTypeNameInJava().
   */
  private String          typeNameInJava;

  private boolean         useClassLevelOtherTags;

  AttributeChangeListener attributeChangeListener = null;

  public MetaMetadataCompositeField()
  {
    // no op
  }

  MetaMetadataCompositeField(String name, HashMapArrayList<String, MetaMetadataField> kids)
  {
    this.setName(name);
    this.getChildrenMap().clear();
    this.getChildrenMap().putAll(kids);
    for (MetaMetadataField kid : kids)
    {
      kid.setParent(this);
    }
  }

  public MetaMetadataCompositeField(MetaMetadataField copy, String name)
  {
    super(copy, name);
  }

  @Override
  protected HashMapArrayList<String, MetaMetadataField> initializeChildrenMap()
  {
    if (getChildrenMap() == null)
    {
      setChildrenMap(new HashMapArrayList<String, MetaMetadataField>());
    }

    return getChildrenMap();
  }

  public String getTypeOrName()
  {
    return getType() == null ? getName() : getType();
  }

  /**
   * Get the MetaMetadataCompositeField associated with this.
   * 
   * @return this, because it is a composite itself.
   */
  @Override
  public MetaMetadataCompositeField metaMetadataCompositeField()
  {
    return this;
  }

  public MMSelectorType getMmSelectorType()
  {
    return mmSelectorType;
  }

  public boolean isGenericMetadata()
  {
    return mmSelectorType == MMSelectorType.SUFFIX_OR_MIME || isBuiltIn();
  }

  public boolean isBuiltIn()
  {
    return false;
  }

  @Override
  protected boolean isInlineDefinition()
  {
    return this.getExtendsAttribute() != null;
  }

  /**
   * this flag: 1) in MetaMetadata, enables class-level @simpl_other_tags to be generated by the
   * compiler; 2) in MetaMetadataComposite, adds a potential other tag to inheritedMmd.
   * 
   * @return
   */
  public boolean isUseClassLevelOtherTags()
  {
    return this.useClassLevelOtherTags;
  }

  protected void setMmSelectorType(MMSelectorType mmSelectorType)
  {
    this.mmSelectorType = mmSelectorType;
  }

  void setUseClassLevelOtherTags(boolean useClassLevelOtherTags)
  {
    this.useClassLevelOtherTags = useClassLevelOtherTags;
  }

  @Override
  public void setType(String type)
  {
    super.setType(type);
    if (attributeChangeListener != null)
      attributeChangeListener.typeChanged(type);
  }

  void setTypeDirectly(String type)
  {
    super.setType(type);
  }

  @Override
  public void setExtendsAttribute(String extendsAttribute)
  {
    super.setExtendsAttribute(extendsAttribute);
    if (attributeChangeListener != null)
      attributeChangeListener.extendsChanged(extendsAttribute);
  }

  void setExtendsAttributeDirectly(String extendsAttribute)
  {
    super.setExtendsAttribute(extendsAttribute);
  }

  @Override
  public void setTag(String tag)
  {
    super.setTag(tag);
    if (attributeChangeListener != null)
      attributeChangeListener.tagChanged(tag);
  }

  void setTagDirectly(String tag)
  {
    super.setTag(tag);
  }

  @Override
  protected String getTypeNameInJava()
  {
    String rst = typeNameInJava;
    if (rst == null)
    {
      rst = XMLTools.classNameFromElementName(getTypeOrName());
      typeNameInJava = rst;
    }
    return typeNameInJava;
  }

  public void setTypeMmd(MetaMetadata typeMmd)
  {
    super.setTypeMmd(typeMmd);
    if (attributeChangeListener != null)
    {
      attributeChangeListener.typeMmdChanged(typeMmd);
    }
  }

  void setTypeMmdDirectly(MetaMetadata typeMmd)
  {
    super.setTypeMmd(typeMmd);
  }

  public void setScope(MmdScope scope)
  {
    super.setScope(scope);
    if (attributeChangeListener != null)
    {
      attributeChangeListener.scopeChanged(scope);
    }
  }

  void setScopeDirectly(MmdScope scope)
  {
    super.setScope(scope);
  }

  @Override
  public void setInlineMmd(MetaMetadata inlineMmd)
  {
    super.setInlineMmd(inlineMmd);
    if (this.attributeChangeListener != null)
    {
      attributeChangeListener.inlineMmdChanged(inlineMmd);
    }
  }

  void setInlineMmdDirectly(MetaMetadata inlineMmd)
  {
    super.setInlineMmd(inlineMmd);
  }

  public void setAttributeChangeListener(AttributeChangeListener attributeChangeListener)
  {
    this.attributeChangeListener = attributeChangeListener;
  }

  @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();

      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(),
                                       this.getFieldType(),
                                       fieldCd,
                                       contextCd,
                                       fieldName,
                                       null,
                                       null,
                                       javaTypeName);
    }
    fd.setWrapped(isWrap());
    this.setMetadataFieldDescriptor(fd);
    return fd;
  }

  @Override
  protected String getFingerprintString()
  {
    StringBuilder sb = StringBuilderUtils.acquire();
    sb.append(super.getFingerprintString());
    addToFp(sb, getType());
    addToFp(sb, getExtendsAttribute());
    addToFp(sb, isWrap());
    String fp = sb.toString();
    StringBuilderUtils.release(sb);
    return fp;
  }

}

@@ -589,24 +589,83 @@
 l_inherit;%0D%0A
+import ecologylab.serialization.annotations.simpl_scalar;%0D%0A
 import ecolo
@@ -1348,16 +1348,79 @@
 logger;%0D
+%0A  %0D%0A  @simpl_scalar%0D%0A  private boolean         notExpandable;%0D
 %0A%0D%0A  sta

Add notExpandable mmd attribute for composites.