content: /**
   * ﻿Copyright (C) 2010 - 2019 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * If the program is linked with libraries which are licensed under one of
   * the following licenses, the combination of the program with the linked
   * library is not considered a "derivative work" of the program:
   *
   *       • Apache License, version 2.0
   *       • Apache Software License, version 1.0
   *       • GNU Lesser General Public License, version 3
   *       • Mozilla Public License, versions 1.0, 1.1 and 2.0
   *       • Common Development and Distribution License (CDDL), version 1.0
   *
   * Therefore the distribution of the program linked with libraries licensed
   * under the aforementioned licenses, is permitted by the copyright holders
   * if the distribution is compliant with both the GNU General Public
   * License version 2 and the aforementioned licenses.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
   * Public License for more details.
   */
  package org.n52.wps.server.r.syntax;
  
  import java.io.IOException;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.LinkedList;
  import java.util.List;
  
  import org.n52.wps.io.data.IData;
  import org.n52.wps.server.r.data.RDataTypeRegistry;
  import org.n52.wps.server.r.data.RTypeDefinition;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * Defines Syntax and Semantics for Annotations in R Skripts
   * 
   * Syntax in (raw) BNF: <RAnnotation> ::= <StartKey> <AttributeSequence> <EndKey> <StartKey> <Attributequence>
   * ::= <RAnnotationTypeInstance>.getStartKey() <RAnnotationTypeInstance>.getAttributeSequence() <EndKey> ::=
   * RSeparator.ANNOTATION_END.getKey() <AttributeSequence> ::= {<RAttributeInstance>.getKey()
   * ATRIBUTE_VALUE_SEPARATOR} <Attributevalue> {ATTRIBUTE_SEPARATOR <RAttributeSequence>}
   * 
   * @author Matthias Hinz
   */
  public class RAnnotation {
  
      private RAnnotationType type;
  
      private HashMap<RAttribute, Object> attributeHash = new HashMap<RAttribute, Object>();
  
      private static Logger LOGGER = LoggerFactory.getLogger(RAnnotation.class);
  
      /**
       * 
       * @param type
       * @param attributeHash
       * @throws IOException
       *         if AttributHash is not valid for any cause
       * @throws RAnnotationException
       */
      public RAnnotation(RAnnotationType type, HashMap<RAttribute, Object> attributeHash) throws IOException,
              RAnnotationException {
          super();
          this.type = type;
          this.attributeHash.putAll(attributeHash);
          this.type.validateDescription(this);
          LOGGER.debug("NEW " + toString());
      }
  
      public RAnnotationType getType() {
          return this.type;
      }
  
      /**
       * 
       * @param attr
       * @return Returns Attribute value as Java Object in case it is more complex
       * @throws RAnnotationException
       */
      public Object getObjectValue(RAttribute attr) throws RAnnotationException {
          Object out = this.attributeHash.get(attr);
  
          if (out == null && attr.getDefValue() != null)
              out = attr.getDefValue();
          else if (attr == RAttribute.ENCODING)
              return getRDataType().getEncoding();
          if (attr == RAttribute.SCHEMA)
              return getRDataType().getSchema();
          return out;
      }
  
      /**
       * 
       * @param attr
       * @return Returns an attribute value as string. Suits for most literal data types
       * @throws RAnnotationException
       */
      public String getStringValue(RAttribute attr) throws RAnnotationException {
          Object value = getObjectValue(attr);
          if (value == null)
              return null;
  
          return value.toString();
      }
  
      public static List<RAnnotation> filterAnnotations(Collection<RAnnotation> annotations,
                                                        RAnnotationType type,
                                                        RAttribute attribute,
                                                        String value) throws RAnnotationException {
          LinkedList<RAnnotation> out = new LinkedList<RAnnotation>();
          for (RAnnotation annotation : annotations) {
              // type filter:
              if (type == null || annotation.getType() == type) {
                  // attribute - value filter:
                  if (attribute == null || value == null || annotation.getStringValue(attribute).equalsIgnoreCase(value)) {
                      out.add(annotation);
                  }
              }
          }
          return out;
      }
  
      public static List<RAnnotation> filterAnnotations(List<RAnnotation> annotations, RAttribute attribute, String value) throws RAnnotationException {
          return filterAnnotations(annotations, null, attribute, value);
      }
  
      public static List<RAnnotation> filterAnnotations(List<RAnnotation> annotations, RAnnotationType type) throws RAnnotationException {
          return filterAnnotations(annotations, type, null, null);
      }
  
      public static RAnnotation filterFirstMatchingAnnotation(List<RAnnotation> annotations,
                                                              RAttribute attribute,
                                                              String value) throws RAnnotationException {
          Iterator<RAnnotation> iterator = filterAnnotations(annotations, null, attribute, value).iterator();
          if (iterator.hasNext())
              return iterator.next();
  
          return null;
      }
  
      public static RAnnotation filterFirstMatchingAnnotation(List<RAnnotation> annotations, RAnnotationType type) throws RAnnotationException {
          Iterator<RAnnotation> iterator = filterAnnotations(annotations, type, null, null).iterator();
          if (iterator.hasNext())
              return iterator.next();
  
          return null;
      }
  
      /**
       * 
       * @param rClass
       *        - value referring to RAttribute.TYPE
       * @return null or supported IData class for rClass - string
       * @throws RAnnotationException
       */
      public static Class< ? extends IData> getDataClass(String rClass) throws RAnnotationException {
          RTypeDefinition rType = RDataTypeRegistry.getInstance().getType(rClass);
          return rType.getIDataClass();
      }
  
      public Class< ? extends IData> getDataClass() throws RAnnotationException {
          String rClass = getStringValue(RAttribute.TYPE);
          return getDataClass(rClass);
      }
  
      /**
       * Checks if the type - argument of an annotation refers to complex data
       * 
       * @return
       * @throws RAnnotationException
       */
      public static boolean isComplex(String rClass) throws RAnnotationException {
          return RDataTypeRegistry.getInstance().getType(rClass).isComplex();
  
      }
  
      public RTypeDefinition getRDataType() throws RAnnotationException {
          return RDataTypeRegistry.getInstance().getType(getStringValue(RAttribute.TYPE));
      }
  
      /**
       * @return true, if the type attribute of an Annotation refers to a complex data type
       * @throws RAnnotationException
       */
      public boolean isComplex() throws RAnnotationException {
          return isComplex(this.getStringValue(RAttribute.TYPE));
      }
  
      /**
       * 
       * @return null or supported ProcessdescriptionType
       * @throws RAnnotationException
       */
      public String getProcessDescriptionType() throws RAnnotationException {
          String type = getStringValue(RAttribute.TYPE);
          RTypeDefinition rdt = RDataTypeRegistry.getInstance().getType(type);
          if (rdt != null)
              return rdt.getProcessKey();
  
          return null;
      }
  
      @Override
      public String toString() {
          StringBuilder builder = new StringBuilder();
          builder.append("RAnnotation [type=");
          builder.append(this.type);
          builder.append(", attributeHash=");
          builder.append(this.attributeHash);
          builder.append("]");
          return builder.toString();
      }
  
      public boolean containsKey(RAttribute key) {
          return this.attributeHash.containsKey(key);
      }
  
      public void setAttribute(RAttribute key, Object value) {
          this.attributeHash.put(key, value);
      }
  }
  