Package org.openntf.domino.graph

Source Code of org.openntf.domino.graph.DominoElement

package org.openntf.domino.graph;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javolution.util.FastMap;
import javolution.util.FastSet;
import javolution.util.function.Equalities;

import org.openntf.domino.Database;
import org.openntf.domino.Document;
import org.openntf.domino.types.BigString;
import org.openntf.domino.types.Null;
import org.openntf.domino.utils.BeanUtils;
import org.openntf.domino.utils.DominoUtils;
import org.openntf.domino.utils.TypeUtils;

import com.tinkerpop.blueprints.Element;

public abstract class DominoElement implements IDominoElement, Serializable {

  public static boolean setReflectiveProperty(final IDominoElement element, final String prop, final Object value) {
    if (prop == null || prop.isEmpty())
      throw new IllegalArgumentException("Cannot set a null or empty property on a DominoElement");
    boolean result = false;
    Object localValues = value;
    Class<? extends Element> elemClass = element.getClass();
    IDominoProperties domProp = IDominoProperties.Reflect.findMappedProperty(elemClass, prop);
    if (domProp != null) {
      localValues = TypeUtils.objectToClass(value, domProp.getType(), null);
    }
    //TODO NTF - first see if it's a mapped property and if so, find out the type and coerce the value to the proper type
    Method setter = BeanUtils.findSetter(elemClass, prop, BeanUtils.toParameterType(localValues));
    if (setter != null) {
      try {
        setter.invoke(element, localValues);
        result = true;
      } catch (Exception e) {
        DominoUtils.handleException(e);
        System.out.println("Unable to invoke " + setter.getName() + " on a " + element.getClass() + " for property: " + prop
            + " with a value of " + String.valueOf(value));
      }
    } else {
      if (domProp != null) {
        element.setProperty(domProp, localValues);
        result = true;
      } else {
        element.setProperty(prop, localValues);
        result = true;
      }
    }
    return result;
  }

  public static Object getReflectiveProperty(final IDominoElement element, final String prop) {
    if (prop == null || prop.isEmpty())
      throw new IllegalArgumentException("Cannot set a null or empty property on a DominoElement");
    Object result = false;
    Class<? extends Element> elemClass = element.getClass();
    Method getter = BeanUtils.findGetter(elemClass, prop);
    if (getter != null) {
      try {
        result = getter.invoke(element, (Object[]) null);
      } catch (Exception e) {
        DominoUtils.handleException(e);
        System.out.println("Unable to invoke " + getter.getName() + " on a " + element.getClass() + " for property: " + prop);
      }
    } else {
      IDominoProperties domProp = IDominoProperties.Reflect.findMappedProperty(elemClass, prop);
      if (domProp != null) {
        result = element.getProperty(domProp);
      } else {
        result = element.getProperty(prop);
      }
    }
    return result;
  }

  public static Object getReflectiveProperty(final IDominoElement element, final IDominoProperties prop) {
    return prop.getType().cast(getReflectiveProperty(element, prop.getName()));
  }

  private static final Logger log_ = Logger.getLogger(DominoElement.class.getName());
  private static final long serialVersionUID = 1L;
  public static final String TYPE_FIELD = "_OPEN_GRAPHTYPE";
  private String key_;
  protected transient DominoGraph parent_;
  private String unid_;
  private Map<String, Serializable> props_;
  public final String[] DEFAULT_STR_ARRAY = { "" };

  public static Document toDocument(final DominoElement element) {
    return element.getRawDocument();
  }

  public static enum Properties implements IDominoProperties {
    TITLE(String.class), KEY(String.class), FORM(String.class);

    private Class<?> type_;

    Properties(final Class<?> type) {
      type_ = type;
    }

    @Override
    public Class<?> getType() {
      return type_;
    }

    @Override
    public String getName() {
      return super.name();
    }
  }

  public DominoElement(final DominoGraph parent, final Document doc) {
    parent_ = parent;

    unid_ = doc.getUniversalID().toUpperCase();
  }

  private transient java.lang.Object lockHolder_;

  public synchronized boolean hasLock() {
    return lockHolder_ != null;
  }

  public synchronized boolean lock(final java.lang.Object lockHolder) {
    if (lockHolder_ == null) {
      lockHolder_ = lockHolder;
      return true;
    }
    return false;
  }

  public synchronized boolean unlock(final java.lang.Object lockHolder) {
    if (lockHolder.equals(lockHolder_)) {
      lockHolder_ = null;
      return true;
    }
    return false;
  }

  synchronized void unlock() {
    lockHolder_ = null;
  }

  public String getTitle() {
    return getProperty(Properties.TITLE, false);
  }

  public void setTitle(final String value) {
    setProperty(Properties.TITLE, value);
  }

  public String getKey() {
    return getProperty(Properties.KEY, false);
  }

  public void setKey(final String value) {
    setProperty(Properties.KEY, value);
  }

  public String getForm() {
    return getProperty(Properties.FORM, false);
  }

  public void setForm(final String value) {
    String current = getForm();
    if (current == null || !current.equalsIgnoreCase(value)) {
      setProperty(Properties.FORM, value);
    }
  }

  private Boolean isNew_;

  void setNew(final boolean isnew) {
    isNew_ = isnew;
  }

  public boolean isNew() {
    if (isNew_ == null) {
      isNew_ = false;
    }
    return isNew_;
  }

  private Map<String, Serializable> getProps() {
    if (props_ == null) {
      props_ = new FastMap<String, Serializable>(Equalities.LEXICAL_CASE_INSENSITIVE).atomic();
    }
    return props_;
  }

  public void addProperty(final String propertyName, final Object value) {
    setProperty(propertyName, value);
  }

  private Database getDatabase() {
    return getParent().getRawDatabase();
  }

  /*
   * (non-Javadoc)
   *
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(final Object o) {
    if (o instanceof DominoElement) {
      return ((DominoElement) o).getId().equals(getId());
    } else {
      return false;
    }
  }

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

  @Override
  public Document getRawDocument() {
    return getDocument();
  }

  public String getRawId() {
    String prefix = getDatabase().getServer() + "!!" + getDatabase().getFilePath();
    return prefix + ": " + getRawDocument().getNoteID();
  }

  @Override
  public int incrementProperty(final String propertyName) {
    Integer result = getProperty(propertyName, Integer.class);
    if (result == null)
      result = 0;
    setProperty(propertyName, ++result);
    return result;
  }

  @Override
  public int decrementProperty(final String propertyName) {
    Integer result = getProperty(propertyName, Integer.class);
    if (result == null)
      result = 0;
    setProperty(propertyName, --result);
    return result;
  }

  private Document getDocument() {
    return getParent().getDocument(unid_, true);
    // Map<String, Document> map = documentCache.get();
    // Document doc = map.get(unid_);
    // if (doc == null) {
    // synchronized (map) {
    // doc = getDatabase().getDocumentByKey(unid_, true);
    // String localUnid = doc.getUniversalID().toUpperCase();
    // if (!unid_.equals(localUnid)) {
    // log_.log(Level.SEVERE, "UNIDs do not match! Expected: " + unid_ + ", Result: " + localUnid);
    // }
    // map.put(unid_, doc);
    // }
    // }
    // return doc;
  }

  @Override
  public String getId() {
    if (key_ == null) {
      key_ = unid_;
    }
    return key_;
  }

  public DominoGraph getParent() {
    return parent_;
  }

  @Override
  public boolean hasProperty(final String key) {
    return getPropertyKeys().contains(key);
  }

  @Override
  public <T> T getProperty(final String key) {
    return getProperty(key, java.lang.Object.class);
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> T getProperty(final String propertyName, final Class<?> T) {
    Object result = null;
    String key = propertyName;
    Map<String, Serializable> props = getProps();
    // synchronized (props) {
    result = props.get(key);
    if (result == null) {
      //      if ("PROGNAME".equalsIgnoreCase(propertyName)) {
      //        System.out.println("DEBUG: " + propertyName + " getting from Document");
      //      }
      try {
        Document doc = getRawDocument();
        result = doc.getItemValue(propertyName, T);
        if (result == null) {
          //          synchronized (props) {
          props.put(key, Null.INSTANCE);
          //          }
        } else if (result instanceof Serializable) {
          //          synchronized (props) {
          props.put(key, (Serializable) result);
          //          }
        } else {
          log_.log(Level.WARNING, "Got a value from the document but it's not Serializable. It's a "
              + result.getClass().getName());
        }
      } catch (Exception e) {
        log_.log(Level.WARNING, "Exception occured attempting to get value from document for " + propertyName
            + " so we cannot return a value", e);
      }
    } else if (result == Null.INSTANCE) {

    } else {
      if (result != null && !T.isAssignableFrom(result.getClass())) {
        //        if ("PROGNAME".equalsIgnoreCase(propertyName)) {
        //          System.out.println("DEBUG: " + propertyName + " result is a " + result.getClass().getSimpleName());
        //        }
        // System.out.println("AH! We have the wrong type in the property cache! How did this happen?");
        try {
          Document doc = getRawDocument();
          result = doc.getItemValue(propertyName, T);
          if (result == null) {
            //            synchronized (props) {
            props.put(key, Null.INSTANCE);
            //            }
          } else if (result instanceof Serializable) {
            //            synchronized (props) {
            props.put(key, (Serializable) result);
            //            }
          }
        } catch (Exception e) {
          log_.log(Level.WARNING, "Exception occured attempting to get value from document for " + propertyName
              + " but we have a value in the cache.", e);
        }
      } else {
        //        if ("PROGNAME".equalsIgnoreCase(propertyName)) {
        //          System.out.println("DEBUG: " + propertyName + " result is a " + result.getClass().getSimpleName());
        //        }
      }
    }
    // }
    //    if (result != null && !T.isAssignableFrom(result.getClass())) {
    //      log_.log(Level.WARNING, "Returning a " + result.getClass().getName() + " when we asked for a " + T.getName());
    //    }
    if (result == Null.INSTANCE) {
      result = null;
    }
    //    if ("PROGNAME".equalsIgnoreCase(propertyName)) {
    //      System.out.println("DEBUG: " + propertyName + " result is a " + (result == null ? "null" : result.getClass().getSimpleName()));
    //    }
    return (T) result;
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> T getProperty(final String propertyName, final Class<?> T, final boolean allowNull) {
    T result = getProperty(propertyName, T);
    if (allowNull) {
      return result;
    } else {
      if (result == null || Null.INSTANCE == result) {
        if (T.isArray())
          if (T.getComponentType() == String.class) {
            return (T) DEFAULT_STR_ARRAY;
          } else {
            return (T) Array.newInstance(T.getComponentType(), 0);
          }
        if (Boolean.class.equals(T) || Boolean.TYPE.equals(T))
          return (T) Boolean.FALSE;
        if (Integer.class.equals(T) || Integer.TYPE.equals(T))
          return (T) Integer.valueOf(0);
        if (Long.class.equals(T) || Long.TYPE.equals(T))
          return (T) Long.valueOf(0l);
        if (Short.class.equals(T) || Short.TYPE.equals(T))
          return (T) Short.valueOf("0");
        if (Double.class.equals(T) || Double.TYPE.equals(T))
          return (T) Double.valueOf(0d);
        if (Float.class.equals(T) || Float.TYPE.equals(T))
          return (T) Float.valueOf(0f);
        if (String.class.equals(T))
          return (T) "";
        try {
          return (T) T.newInstance();
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
      } else {
        return result;
      }
    }
  }

  @Override
  public Set<String> getPropertyKeys() {

    return getPropertyKeys(true);
  }

  private FastSet<String> propKeys_;

  private FastSet<String> getPropKeysInt() {
    if (propKeys_ == null) {
      propKeys_ = new FastSet<String>(Equalities.LEXICAL_CASE_INSENSITIVE).atomic();
    }
    return propKeys_;
  }

  @Override
  public Set<String> getPropertyKeys(final boolean includeEdgeFields) {  //TODO NTF fix?
    if (getPropKeysInt().isEmpty()) {
      getPropKeysInt().addAll(getRawDocument().keySet());
    }
    if (includeEdgeFields) {
      return getPropKeysInt().unmodifiable();
    } else {
      FastSet<String> result = new FastSet<String>(Equalities.LEXICAL_CASE_INSENSITIVE);
      for (String name : getPropKeysInt()) {
        if (!(name.startsWith(DominoVertex.IN_PREFIX) || name.startsWith(DominoVertex.OUT_PREFIX))) {
          result.add(name);
        }
      }
      return result.unmodifiable();
    }
  }

  @Override
  public abstract void remove();

  //  {
  //    getParent().startTransaction(this);
  //    getRawDocument().removePermanently(true);
  //  }

  void _remove() {
    getParent().startTransaction(this);
    getRawDocument().removePermanently(true);
  }

  private FastSet<String> removedProperties_;

  private FastSet<String> getRemovedPropertiesInt() {
    if (removedProperties_ == null) {
      removedProperties_ = new FastSet<String>(Equalities.LEXICAL_CASE_INSENSITIVE).atomic();
    }
    return removedProperties_;
  }

  @Override
  public <T> T removeProperty(final String key) {
    getParent().startTransaction(this);
    T result = getProperty(key);
    Map<String, Serializable> props = getProps();
    //    synchronized (props) {
    props.remove(key);
    //    }
    Document doc = getRawDocument();
    synchronized (doc) {
      doc.removeItem(key);
    }
    //    synchronized (removedProperties_) {
    getRemovedPropertiesInt().add(key);
    //    }
    //    synchronized (propKeys_) {
    getPropKeysInt().remove(key);
    //    }
    return result;
  }

  // public void save() {
  // getRawDocument().save();
  // }

  @Override
  public void setRawDocument(final org.openntf.domino.Document doc) {
    unid_ = doc.getUniversalID().toUpperCase();
  }

  private FastSet<String> changedProperties_;

  private FastSet<String> getChangedPropertiesInt() {
    if (changedProperties_ == null) {
      changedProperties_ = new FastSet<String>(Equalities.LEXICAL_CASE_INSENSITIVE).atomic();
    }
    return changedProperties_;
  }

  //  void setProperty(final String propertyName, final java.lang.Object value, final boolean force) {
  //
  //  }

  @Override
  public void setProperty(final String propertyName, final java.lang.Object value) {
    //    if ("PROGNAME".equalsIgnoreCase(propertyName)) {
    //      System.out.println("DEBUG Setting " + propertyName);
    //    }
    boolean isEdgeCollection = false;
    boolean isEqual = false;
    String key = propertyName;
    Map<String, Serializable> props = getProps();
    Object old = null;
    if (props != null) {
      if (propertyName != null) {

        //        synchronized (propKeys_) {
        getPropKeysInt().add(propertyName);
        //        }
        Object current = getProperty(propertyName);
        if (propertyName.startsWith(DominoVertex.IN_PREFIX) && value instanceof java.util.Collection) {
          isEdgeCollection = true;
        }
        if (current == null && value == null) {
          return;
        }
        if (value != null && current != null) {
          if (!(value instanceof java.util.Collection) && !(value instanceof java.util.Map) && !value.getClass().isArray()) {
            isEqual = value.equals(current);
          }
        }
        if (isEqual) {
          log_.log(Level.FINE, "Not setting property " + propertyName + " because the new value is equal to the existing value");
        }
        boolean changeMade = false;
        //        synchronized (props) {

        if (value instanceof Serializable) {
          if (current == null || Null.INSTANCE.equals(current)) {
            //              if ("PROGNAME".equalsIgnoreCase(propertyName)) {
            //                System.out.println("DEBUG: " + propertyName + " checking FROM NULL values from " + String.valueOf(current)
            //                    + " to " + String.valueOf(value));
            //              }
            getParent().startTransaction(this);
            old = props.put(key, (Serializable) value);
            //              synchronized (changedProperties_) {
            getChangedPropertiesInt().add(propertyName);
            //              }
          } else if (!isEqual) {
            getParent().startTransaction(this);
            old = props.put(key, (Serializable) value);
            //              synchronized (changedProperties_) {
            getChangedPropertiesInt().add(propertyName);
            //              }
          } else {
            //              if ("PROGNAME".equalsIgnoreCase(propertyName)) {
            //                System.out.println("DEBUG: " + propertyName + " equal?? values match from " + String.valueOf(current)
            //                    + " to " + String.valueOf(value));
            //              }
          }
        } else if (value == null) {
          if (current != null && !current.equals(Null.INSTANCE)) {
            getParent().startTransaction(this);
            old = props.put(key, Null.INSTANCE);
            //              synchronized (changedProperties_) {
            getChangedPropertiesInt().add(propertyName);
            //              }
          }
        } else {
          //            if ("PROGNAME".equalsIgnoreCase(propertyName)) {
          //              System.out.println("DEBUG: " + propertyName + " values from " + String.valueOf(current) + " to "
          //                  + String.valueOf(value));
          //            }
          log_.log(Level.WARNING, "Attempted to set property " + propertyName + " to a non-serializable value: "
              + value.getClass().getName());
        }
        //        }

      } else {
        log_.log(Level.WARNING, "propertyName is null on a setProperty request?");
      }
    } else {
      log_.log(Level.WARNING, "Properties are null for element!");
    }
  }

  protected void reapplyChanges() {
    Map<String, Serializable> props = getProps();
    Document doc = getDocument();
    //    synchronized (props) {
    if (props.isEmpty()) {
      // System.out.println("Cached properties is empty!");
    } else {
      //        synchronized (changedProperties_) {
      // System.out.println("Re-applying cached properties: " + changedProperties_.size());
      for (String s : getChangedPropertiesInt()) {
        //        CharSequence key = new CaseInsensitiveString(s);
        String key = s;
        Object v = props.get(key);
        if (v == null) {
          // System.out.println("Writing a null value for property: " + key
          // + " to an Element document. Probably not good...");
        }
        if (s.startsWith(DominoVertex.IN_PREFIX) || s.startsWith(DominoVertex.OUT_PREFIX)) {
          doc.replaceItemValue(s, v, false);
        } else {
          doc.replaceItemValue(s, v);
        }
      }
      getChangedPropertiesInt().clear();
      //        }

    }
    //    }
    //    synchronized (removedProperties_) {
    for (String key : getRemovedPropertiesInt()) {
      doc.removeItem(key);
    }
    //    }
  }

  @Override
  public int incrementProperty(final IDominoProperties prop) {
    return incrementProperty(prop.getName());
  }

  @Override
  public int decrementProperty(final IDominoProperties prop) {
    return decrementProperty(prop.getName());
  }

  @Override
  public <T> T getProperty(final IDominoProperties prop) {
    if (prop == null) {
      log_.log(Level.WARNING, "getProperty was called with a null argument, therefore it's impossible to return a property.");
      return null;
    }
    Class<?> type = prop.getType();
    Object result = getProperty(prop.getName(), type);
    if (result != null && type.isAssignableFrom(result.getClass())) {
      return (T) type.cast(result);
    } else {
      // System.out.println("Property returned a " + (result == null ? "null" : result.getClass().getName())
      // + " even though we requested a " + type.getName());
    }
    return (T) result;
  }

  @Override
  public <T> T getProperty(final IDominoProperties prop, final boolean allowNull) {
    Class<?> type = prop.getType();
    Object result = getProperty(prop.getName(), type, allowNull);
    if (result != null && type.isAssignableFrom(result.getClass())) {
      return (T) type.cast(result);
    } else {
      // System.out.println("Property returned a " + (result == null ? "null" : result.getClass().getName())
      // + " even though we requested a " + type.getName());
    }
    return (T) result;
  }

  @Override
  public void setProperty(final IDominoProperties prop, final java.lang.Object value) {
    Object current = getProperty(prop, true);
    if (current == null || !current.equals(value)) {
      setProperty(prop.getName(), value);
    }
  }

  public static Object fromMapValue(final String key, final Object value) {
    Object result = value;

    return result;
  }

  public static Object toMapValue(final Object value) {
    Object result = value;
    if (EnumSet.class.isAssignableFrom(value.getClass())) {
      System.out.println("DEBUG: Mapping an EnumSet");
      if (!((EnumSet<?>) value).isEmpty()) {
        StringBuilder eListing = new StringBuilder();
        eListing.append('[');
        for (Object rawEnum : (EnumSet<?>) value) {
          if (Enum.class.isAssignableFrom(rawEnum.getClass())) {
            eListing.append(((Enum<?>) rawEnum).name());
          } else {
            eListing.append("ERROR: expected Enum was a " + rawEnum.getClass().getName());
          }
          eListing.append(',');
        }
        eListing.deleteCharAt(eListing.length() - 1);
        eListing.append(']');
        result = eListing.toString();
      } else {
        result = "";
      }
    } else if (Enum.class.isAssignableFrom(value.getClass())) {
      result = ((Enum<?>) value).name();
    } else if (CharSequence.class.isAssignableFrom(value.getClass())) {
      result = ((CharSequence) value).toString();
    } else if (BigString.class.isAssignableFrom(value.getClass())) {
      result = ((BigString) value).toString();
    } else {
      result = value;
    }
    return result;
  }

  public Map<String, Object> toMap(final IDominoProperties[] props, final byte keyStyle) {
    Map<String, Object> result = new LinkedHashMap<String, Object>();
    for (IDominoProperties prop : props) {
      String mapKey = prop.getName();
      if (keyStyle == Character.LOWERCASE_LETTER) {
        mapKey = mapKey.toLowerCase();
      } else if (keyStyle == Character.UPPERCASE_LETTER) {
        mapKey = mapKey.toUpperCase();
      }
      Object value = getProperty(prop, true);
      if (value != null) {
        result.put(mapKey, toMapValue(value));
      }
    }
    return result;
  }

  @Override
  public Map<String, Object> toMap(final IDominoProperties[] props) {
    return toMap(props, (byte) 0);
  }

  public Map<String, Object> toMap(final Set<IDominoProperties> props, final byte keyStyle) {
    return toMap(props.toArray(new IDominoProperties[props.size()]), keyStyle);
  }

  @Override
  public Map<String, Object> toMap(final Set<IDominoProperties> props) {
    return toMap(props, (byte) 0);
  }

  public boolean fromMap(final Map<String, Object> map) {
    boolean result = true;
    for (String key : map.keySet()) {
      boolean success = DominoElement.setReflectiveProperty(this, key, map.get(key));
      if (!success)
        result = false;
    }
    return result;
  }

}
TOP

Related Classes of org.openntf.domino.graph.DominoElement

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.