All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.datatorrent.api.Attribute Maven / Gradle / Ivy

There is a newer version: 3.7.0
Show newest version
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 com.datatorrent.api;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.google.common.base.Throwables;

/**
 * Attribute represents the attribute which can be set on various components in the system.
 *
 * @param  type of the value which can be stored against the attribute.
 * @since 2.0.0
 */
public class Attribute implements Serializable
{
  public final T defaultValue;
  public final String name;
  public final StringCodec codec;

  public Attribute(StringCodec codec)
  {
    this(null, null, codec);
  }

  public Attribute(T defaultValue)
  {
    this(null, defaultValue, null);
  }

  public Attribute(T defaultValue, StringCodec codec)
  {
    this(null, defaultValue, codec);
  }

  private Attribute(String name, T defaultValue, StringCodec codec)
  {
    this.name = name;
    this.defaultValue = defaultValue;
    this.codec = codec;
  }

  @Override
  public int hashCode()
  {
    return name.hashCode();
  }

  @Override
  public boolean equals(Object obj)
  {
    if (obj == null) {
      return false;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }
    @SuppressWarnings(value = "unchecked")
    final Attribute other = (Attribute)obj;
    return !((this.name == null) ? (other.name != null) : !this.name.equals(other.name));
  }

  public String getName()
  {
    return "attr" + name.substring(name.lastIndexOf('.'));
  }

  public String getSimpleName()
  {
    return name.substring(name.lastIndexOf('.') + 1);
  }

  @Override
  public String toString()
  {
    return "Attribute{" + "defaultValue=" + defaultValue + ", name=" + name + ", codec=" + codec + '}';
  }

  private static final long serialVersionUID = 201310111904L;

  /**
   * Parameterized and scoped context attribute map that supports serialization.
   * Derived from io.netty.util.AttributeMap
   *
   * @since 0.3.2
   */
  public interface AttributeMap extends Cloneable
  {
    /**
     * Return the attribute value for the given key. If the map does not have an
     * entry for the key, null is returned.
     *
     * @param 
     * @param key
     * @return AttributeValue
     */
     T get(Attribute key);

    /**
     * Return true if value for an attribute is set. It returns false if the
     * value for the attribute was never set.
     *
     * @param key attribute key
     * @return true if the value for the attribute was set, false otherwise.
     */
    boolean contains(Attribute key);

    /**
     * Assign value for a particular attributes.
     *
     * @param    Type of the value
     * @param key   Attribute which is being assigned the value
     * @param value Value which is being assigned.
     * @return Previous value against the attribute or null if it was not assigned.
     */
     T put(Attribute key, T value);

    Set, Object>> entrySet();

    /**
     * Clone the current map.
     * @return a shallow copy of this AtrributeMap.
     * @throws java.lang.CloneNotSupportedException
     */
    AttributeMap clone() throws CloneNotSupportedException;

    /**
     * DefaultAttributeMap is the default implementation of AttributeMap. It's backed by a map internally.
     */
    class DefaultAttributeMap implements AttributeMap, Serializable
    {
      private HashMap, Object> map;

      public DefaultAttributeMap()
      {
        this(new HashMap, Object>());
      }

      private DefaultAttributeMap(HashMap, Object> map)
      {
        super();
        this.map = map;
      }

      @Override
      @SuppressWarnings(value = "unchecked")
      public  T get(Attribute key)
      {
        return (T)map.get(key);
      }

      @Override
      public String toString()
      {
        return this.map.toString();
      }

      @Override
      @SuppressWarnings("unchecked")
      public DefaultAttributeMap clone() throws CloneNotSupportedException
      {
        DefaultAttributeMap clone = (DefaultAttributeMap)super.clone();
        clone.map = (HashMap, Object>)map.clone();
        return clone;
      }

      @Override
      @SuppressWarnings(value = "unchecked")
      public  T put(Attribute key, T value)
      {
        return (T)map.put(key, value);
      }

      @Override
      public Set, Object>> entrySet()
      {
        return map.entrySet();
      }

      @Override
      public boolean equals(Object o)
      {
        if (this == o) {
          return true;
        }
        if (!(o instanceof DefaultAttributeMap)) {
          return false;
        }
        DefaultAttributeMap that = (DefaultAttributeMap)o;
        return !(map != null ? !map.equals(that.map) : that.map != null);
      }

      @Override
      public int hashCode()
      {
        return map != null ? map.hashCode() : 0;
      }

      @Override
      public boolean contains(Attribute key)
      {
        return map.containsKey(key);
      }

      private static final long serialVersionUID = 201306051022L;
    }

    /**
     * This class inspects and initializes the attributes with their field names so that they can be used
     * from properties files.
     *
     * Engine uses it internally to initialize the Interfaces that may have Attributes defined in them.
     */
    class AttributeInitializer
    {
      static final HashMap, Set>> map = new HashMap<>();

      public static Map, Object> getAllAttributes(Context context, Class clazz)
      {
        Map, Object> result = new HashMap<>();
        try {
          for (Field f: clazz.getDeclaredFields()) {
            if (Modifier.isStatic(f.getModifiers()) && Attribute.class.isAssignableFrom(f.getType())) {
              @SuppressWarnings(value = "unchecked")
              Attribute attribute = (Attribute)f.get(null);
              result.put(attribute, context.getValue(attribute));
            }
          }
        } catch (Exception ex) {
          throw Throwables.propagate(ex);
        }
        return result;
      }

      public static Set> getAttributes(Class clazz)
      {
        return map.get(clazz);
      }

      /**
       * Initialize the static attributes defined in the class.
       *
       * @param clazz class whose static attributes need to be initialized.
       * @return 0 if the clazz was already initialized, identity hash code of the clazz otherwise.
       */
      /* both for Enum2String */
      @SuppressWarnings(value = {"unchecked", "rawtypes"})
      public static long initialize(final Class clazz)
      {
        if (map.containsKey(clazz)) {
          return 0;
        }
        Set> set = new HashSet<>();
        try {
          for (Field f: clazz.getDeclaredFields()) {
            if (Modifier.isStatic(f.getModifiers()) && Attribute.class.isAssignableFrom(f.getType())) {
              Attribute attribute = (Attribute)f.get(null);
              if (attribute.name == null) {
                Field nameField = Attribute.class.getDeclaredField("name");
                nameField.setAccessible(true);
                nameField.set(attribute, clazz.getCanonicalName() + '.' + f.getName());
                nameField.setAccessible(false);
              }
              /* Handle trivial cases here even though this may spoil API users. */
              if (attribute.codec == null) {
                StringCodec codec = null;
                if (attribute.defaultValue != null) {
                  Class klass = attribute.defaultValue.getClass();
                  codec = StringCodec.Factory.getInstance(klass);
                }
                if (codec != null) {
                  Field codecField = Attribute.class.getDeclaredField("codec");
                  codecField.setAccessible(true);
                  codecField.set(attribute, codec);
                  codecField.setAccessible(false);
                }
              }
              set.add(attribute);
            }
          }
        } catch (Exception ex) {
          throw Throwables.propagate(ex);
        }
        map.put(clazz, set);
        return (long)clazz.getModifiers() << 32 | clazz.hashCode();
      }

    }

  }

}