/*
 * Copyright 2008 Lukas Lang
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 *
 */
package org.exolab.castor.builder.info.nature;

import java.util.List;

import org.castor.core.nature.BaseNature;
import org.castor.core.nature.PropertyHolder;
import org.exolab.castor.builder.info.GroupInfo;
import org.exolab.castor.builder.info.NodeType;
import org.exolab.castor.builder.types.XSType;

/**
 * A XML specific view of a {@link PropertyHolder}, which can be a {@link ClassInfo} or a
 * {@link FieldInfo}. Property based implementation.
 * 
 * @author Lukas Lang
 * @since 1.2.1
 */
public final class XMLInfoNature extends BaseNature {

  /**
   * Property namespace prefix.
   */
  private static final String NAMESPACE_PREFIX = "namespaceprefix";

  /**
   * Property namespace URI.
   */
  private static final String NAMESPACE_URI = "namespaceuri";

  /**
   * Property node name.
   */
  private static final String NODE_NAME = "nodename";

  /**
   * Property node type.
   */
  private static final String NODE_TYPE = "nodetype";

  /**
   * Property schema type.
   */
  private static final String SCHEMA_TYPE = "schematype";

  /**
   * Indicates XML schema definition is global element or element with anonymous type.
   */
  private static final String ELEMENT_DEFINITION = "elementdefinition";

  /**
   * A flag indicating if the object described by this XML info can appear more than once.
   */
  private static final String MULTIVALUED = "multivalued";

  /**
   * Indicates the XML object must appear at least once.
   */
  private static final String REQUIRED = "required";

  /**
   * Property substitution groups.
   */
  private static final String SUBSTITUTION_GROUP = "substitutionGroup";

  /**
   * Property container.
   */
  private static final String IS_CONTAINER = "isContainer";

  /**
   * Property group info.
   */
  private static final String GROUP_INFO = "groupInfo";

  /**
   * Constructor taking a PropertyHolder.
   * 
   * @param holder in focus.
   */
  public XMLInfoNature(final PropertyHolder holder) {
    super(holder);
  }

  /**
   * Implementation returns the fully qualified class name.
   * 
   * @return the Nature id.
   * @see org.exolab.castor.builder.info.nature.Nature#getId()
   */
  public String getId() {
    return this.getClass().getName();
  }

  /**
   * Returns the namespace prefix of the object described by this XMLInfo.
   * 
   * @return the namespace prefix of the object described by this XMLInfo
   */
  public String getNamespacePrefix() {
    return (String) this.getProperty(NAMESPACE_PREFIX);
  }

  /**
   * Returns the namespace URI of the object described by this XMLInfo.
   * 
   * @return the namespace URI of the object described by this XMLInfo
   */
  public String getNamespaceURI() {
    return (String) this.getProperty(NAMESPACE_URI);
  }

  /**
   * Returns the XML name for the object described by this XMLInfo.
   * 
   * @return the XML name for the object described by this XMLInfo, or null if no name has been set
   */
  public String getNodeName() {
    return (String) this.getProperty(NODE_NAME);
  }

  /**
   * Returns the node type for the object described by this XMLInfo.
   * <code>XMLInfo.ELEMENT_TYPE</code> if property is not set.
   * 
   * @return the node type for the object described by this XMLInfo
   */
  public NodeType getNodeType() {
    NodeType nodeType = (NodeType) this.getProperty(NODE_TYPE);
    if (nodeType == null) {
      return NodeType.ELEMENT;
    }
    return nodeType;
  }

  /**
   * Returns the string name of the nodeType, either "attribute", "element" or "text".
   * 
   * @return the name of the node-type of the object described by this XMLInfo.
   */
  public String getNodeTypeName() {
    NodeType nodeType = getNodeType();
    switch (nodeType) {
      case ATTRIBUTE:
        return "attribute";
      case ELEMENT:
        return "element";
      case TEXT:
        return "text";
      default:
        return "unknown";
    }
  }

  /**
   * Returns the XML Schema type for the described object.
   * 
   * @return the XML Schema type.
   */
  public XSType getSchemaType() {
    return (XSType) this.getProperty(SCHEMA_TYPE);
  }

  /**
   * Returns true if XSD is global element or element with anonymous type or false if property is
   * not set.
   * 
   * @return true if xsd is element, false if not or null.
   */
  public boolean isElementDefinition() {
    return getBooleanPropertyDefaultFalse(ELEMENT_DEFINITION);
  }

  /**
   * Returns whether or not the object described by this XMLInfo is multi-valued (appears more than
   * once in the XML document). Returns false if the property was not set.
   * 
   * @return true if this object can appear more than once, false if not or not set.
   */
  public boolean isMultivalued() {
    return getBooleanPropertyDefaultFalse(MULTIVALUED);
  }

  /**
   * Return true if the XML object described by this XMLInfo must appear at least once in the XML
   * document (or object model). Returns false if the property was not set.
   * 
   * @return true if the XML object must appear at least once, false if not or not set.
   */
  public boolean isRequired() {
    return getBooleanPropertyDefaultFalse(REQUIRED);
  }

  /**
   * Sets whether or not XSD is element or not.
   * 
   * @param elementDef The flag indicating whether or not XSD is global element, element with
   *        anonymous type or not.
   */
  public void setElementDefinition(final boolean elementDef) {
    this.setProperty(ELEMENT_DEFINITION, Boolean.valueOf(elementDef));
  }

  /**
   * Sets whether the XML object can appear more than once in the XML document.
   * 
   * @param multivalued The boolean indicating whether or not the object can appear more than once.
   */
  public void setMultivalued(final boolean multivalued) {
    this.setProperty(MULTIVALUED, Boolean.valueOf(multivalued));
  }

  /**
   * Sets the desired namespace prefix for this XMLInfo There is no guarantee that this prefix will
   * be used.
   * 
   * @param nsPrefix the desired namespace prefix
   */
  public void setNamespacePrefix(final String nsPrefix) {
    this.setProperty(NAMESPACE_PREFIX, nsPrefix);
  }

  /**
   * Sets the Namespace URI for this XMLInfo.
   * 
   * @param nsURI the Namespace URI for this XMLInfo
   */
  public void setNamespaceURI(final String nsURI) {
    this.setProperty(NAMESPACE_URI, nsURI);
  }

  /**
   * Sets the XML name of the object described by this XMLInfo.
   * 
   * @param name the XML node name of the described object.
   */
  public void setNodeName(final String name) {
    this.setProperty(NODE_NAME, name);
  }

  /**
   * Sets the nodeType for this XMLInfo.
   * 
   * @param nodeType the node type of the described object
   */
  public void setNodeType(final NodeType nodeType) {
    this.setProperty(NODE_TYPE, nodeType);
  }

  /**
   * Sets whether or not the XML object must appear at least once.
   * 
   * @param required the flag indicating whether or not this XML object is required
   */
  public void setRequired(final boolean required) {
    this.setProperty(REQUIRED, Boolean.valueOf(required));
  }

  /**
   * Sets the XML Schema type for this XMLInfo.
   * 
   * @param xsType the XML Schema type
   */
  public void setSchemaType(final XSType xsType) {
    this.setProperty(SCHEMA_TYPE, xsType);
  }

  /**
   * Returns the possible substitution groups.
   * 
   * @return the possible substitution groups.
   */
  @SuppressWarnings("unchecked")
  public List<String> getSubstitutionGroups() {
    return getPropertyAsList(SUBSTITUTION_GROUP);
  }

  /**
   * Sets the possible substitution groups.
   * 
   * @param substitutionGroups Possible substitution groups.
   */
  public void setSubstitutionGroups(final List<String> substitutionGroups) {
    this.setProperty(SUBSTITUTION_GROUP, substitutionGroups);
  }

  /**
   * Returns true if this ClassInfo describes a container class. A container class is a class which
   * should not be marshalled as XML, but whose members should be.
   *
   * @return true if this ClassInfo describes a container class.
   */
  public boolean isContainer() {
    return this.getBooleanPropertyDefaultFalse(IS_CONTAINER);
  }

  /**
   * Sets whether or not this ClassInfo describes a container class. A container class is a class
   * which should not be marshalled as XML, but whose members should be. By default this is false.
   *
   * @param isContainer the boolean value when true indicates this class should be a container
   *        class.
   */
  public void setContainer(final boolean isContainer) {
    this.setProperty(IS_CONTAINER, Boolean.valueOf(isContainer));
  }

  /**
   * Returns the {@link GroupInfo} for this XML nature.
   *
   * @return the {@link GroupInfo} instance.
   */
  public GroupInfo getGroupInfo() {
    return (GroupInfo) this.getProperty(GROUP_INFO);
  }

  /**
   * Sets the {@link GroupInfo} for this XML nature.
   *
   * @param groupInfo the {@link GroupInfo} instance.
   */
  public void setGroupInfo(final GroupInfo groupInfo) {
    this.setProperty(GROUP_INFO, groupInfo);
  }

  /**
   * Returns true if the compositor of this GroupInfo is a choice.
   *
   * @return true if the compositor of this GroupInfo is a choice
   */
  public boolean isChoice() {
    return getGroupInfo().isChoice();
  }

  /**
   * Returns true if the compositor of this GroupInfo is a sequence.
   *
   * @return true if the compositor of this GroupInfo is a sequence
   */
  public boolean isSequence() {
    return getGroupInfo().isSequence();
  }


}
