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

com.orientechnologies.orient.object.enhancement.OObjectEntityEnhancer Maven / Gradle / Ivy

The newest version!
/*
 *
 * Copyright 2012 Luca Molino (molino.luca--AT--gmail.com)
 *
 * 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 com.orientechnologies.orient.object.enhancement;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.Proxy;
import javassist.util.proxy.ProxyFactory;

import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.db.object.ODatabaseObject;
import com.orientechnologies.orient.core.entity.OEntityManager;
import com.orientechnologies.orient.core.exception.OSerializationException;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.object.serialization.OObjectCustomSerializerList;
import com.orientechnologies.orient.object.serialization.OObjectCustomSerializerMap;
import com.orientechnologies.orient.object.serialization.OObjectCustomSerializerSet;

/**
 * @author luca.molino
 * 
 */
public class OObjectEntityEnhancer {

  private static final OObjectEntityEnhancer instance              = new OObjectEntityEnhancer();

  public static final String                 ENHANCER_CLASS_PREFIX = "orientdb_";

  public OObjectEntityEnhancer() {
  }

  @SuppressWarnings("unchecked")
  public  T getProxiedInstance(final String iClass, final OEntityManager entityManager, final ODocument doc, Object... iArgs) {
    final Class clazz = (Class) entityManager.getEntityClass(iClass);
    return getProxiedInstance(clazz, doc, iArgs);
  }

  @SuppressWarnings("unchecked")
  public  T getProxiedInstance(final String iClass, final Object iEnclosingInstance, final OEntityManager entityManager,
      final ODocument doc, Object... iArgs) {
    final Class clazz = (Class) entityManager.getEntityClass(iClass);
    return getProxiedInstance(clazz, iEnclosingInstance, doc, iArgs);
  }

  public  T getProxiedInstance(final Class iClass, final ODocument doc, Object... iArgs) {
    return getProxiedInstance(iClass, null, doc, iArgs);
  }

  @SuppressWarnings("unchecked")
  public  T getProxiedInstance(final Class iClass, Object iEnclosingInstance, final ODocument doc, Object... iArgs) {
    if (iClass == null) {
      throw new OSerializationException("Type " + doc.getClassName()
          + " cannot be serialized because is not part of registered entities. To fix this error register this class");
    }
    final Class c;
    boolean isInnerClass = iClass.getEnclosingClass() != null;
    if (Proxy.class.isAssignableFrom(iClass)) {
      c = iClass;
    } else {
      ProxyFactory f = new ProxyFactory();
      f.setSuperclass(iClass);
      f.setFilter(new MethodFilter() {
        public boolean isHandled(Method m) {
          final String methodName = m.getName();
          try {
            return (isSetterMethod(methodName, m) || isGetterMethod(methodName, m) || methodName.equals("equals") || methodName
                .equals("hashCode"));
          } catch (NoSuchFieldException nsfe) {
            OLogManager.instance().warn(this, "Error handling the method %s in class %s", nsfe, m.getName(), iClass.getName());
            return false;
          } catch (SecurityException se) {
            OLogManager.instance().warn(this, "", se, m.getName(), iClass.getName());
            return false;
          }
        }
      });
      c = f.createClass();
    }
    MethodHandler mi = new OObjectProxyMethodHandler(doc);
    try {
      T newEntity;
      if (iArgs != null && iArgs.length > 0) {
        if (isInnerClass) {
          if (iEnclosingInstance == null) {
            iEnclosingInstance = iClass.getEnclosingClass().newInstance();
          }
          Object[] newArgs = new Object[iArgs.length + 1];
          newArgs[0] = iEnclosingInstance;
          for (int i = 0; i < iArgs.length; i++) {
            newArgs[i + 1] = iArgs[i];
          }
          iArgs = newArgs;
        }
        Constructor constructor = null;
        for (Constructor constr : c.getConstructors()) {
          boolean found = true;
          if (constr.getParameterTypes().length == iArgs.length) {
            for (int i = 0; i < constr.getParameterTypes().length; i++) {
              Class parameterType = constr.getParameterTypes()[i];
              if (parameterType.isPrimitive()) {
                if (!isPrimitiveParameterCorrect(parameterType, iArgs[i])) {
                  found = false;
                  break;
                }
              } else if (iArgs[i] != null && !parameterType.isAssignableFrom(iArgs[i].getClass())) {
                found = false;
                break;
              }
            }
          } else {
            continue;
          }
          if (found) {
            constructor = (Constructor) constr;
            break;
          }
        }
        if (constructor != null) {
          newEntity = (T) constructor.newInstance(iArgs);
          initDocument(iClass, newEntity, doc, (ODatabaseObject) ODatabaseRecordThreadLocal.INSTANCE.get().getDatabaseOwner());
        } else {
          if (iEnclosingInstance != null)
            newEntity = createInstanceNoParameters(c, iEnclosingInstance);
          else
            newEntity = createInstanceNoParameters(c, iClass);
        }
      } else {
        if (iEnclosingInstance != null)
          newEntity = createInstanceNoParameters(c, iEnclosingInstance);
        else
          newEntity = createInstanceNoParameters(c, iClass);
      }
      ((Proxy) newEntity).setHandler(mi);
      if (OObjectEntitySerializer.hasBoundedDocumentField(iClass))
        OObjectEntitySerializer.setFieldValue(OObjectEntitySerializer.getBoundedDocumentField(iClass), newEntity, doc);
      return newEntity;
    } catch (InstantiationException ie) {
      OLogManager.instance().error(this, "Error creating proxied instance for class " + iClass.getName(), ie);
    } catch (IllegalAccessException iae) {
      OLogManager.instance().error(this, "Error creating proxied instance for class " + iClass.getName(), iae);
    } catch (IllegalArgumentException iae) {
      OLogManager.instance().error(this, "Error creating proxied instance for class " + iClass.getName(), iae);
    } catch (SecurityException se) {
      OLogManager.instance().error(this, "Error creating proxied instance for class " + iClass.getName(), se);
    } catch (InvocationTargetException ite) {
      OLogManager.instance().error(this, "Error creating proxied instance for class " + iClass.getName(), ite);
    } catch (NoSuchMethodException nsme) {
      OLogManager.instance().error(this, "Error creating proxied instance for class " + iClass.getName(), nsme);
    }
    return null;
  }

  public static synchronized OObjectEntityEnhancer getInstance() {
    return instance;
  }

  private boolean isSetterMethod(String fieldName, Method m) throws SecurityException, NoSuchFieldException {
    if (!fieldName.startsWith("set") || !checkIfFirstCharAfterPrefixIsUpperCase(fieldName, "set"))
      return false;
    if (m.getParameterTypes() != null && m.getParameterTypes().length != 1)
      return false;
    return !OObjectEntitySerializer.isTransientField(m.getDeclaringClass(), getFieldName(m));
  }

  private boolean isGetterMethod(String fieldName, Method m) throws SecurityException, NoSuchFieldException {
    int prefixLength;
    if (fieldName.startsWith("get") && checkIfFirstCharAfterPrefixIsUpperCase(fieldName, "get"))
      prefixLength = "get".length();
    else if (fieldName.startsWith("is") && checkIfFirstCharAfterPrefixIsUpperCase(fieldName, "is"))
      prefixLength = "is".length();
    else
      return false;
    if (m.getParameterTypes() != null && m.getParameterTypes().length > 0)
      return false;
    if (fieldName.length() <= prefixLength)
      return false;
    return !OObjectEntitySerializer.isTransientField(m.getDeclaringClass(), getFieldName(m));
  }

  protected String getFieldName(Method m) {
    if (m.getName().startsWith("get"))
      return getFieldName(m.getName(), "get");
    else if (m.getName().startsWith("set"))
      return getFieldName(m.getName(), "set");
    else
      return getFieldName(m.getName(), "is");
  }

  protected String getFieldName(String methodName, String prefix) {
    StringBuffer fieldName = new StringBuffer();
    fieldName.append(Character.toLowerCase(methodName.charAt(prefix.length())));
    for (int i = (prefix.length() + 1); i < methodName.length(); i++) {
      fieldName.append(methodName.charAt(i));
    }
    return fieldName.toString();
  }

  private boolean checkIfFirstCharAfterPrefixIsUpperCase(String methodName, String prefix) {
    return methodName.length() > prefix.length() ? Character.isUpperCase(methodName.charAt(prefix.length())) : false;
  }

  private boolean isPrimitiveParameterCorrect(Class primitiveClass, Object parameterValue) {
    if (parameterValue == null)
      return false;
    final Class parameterClass = parameterValue.getClass();
    if (Integer.TYPE.isAssignableFrom(primitiveClass))
      return Integer.class.isAssignableFrom(parameterClass);
    else if (Double.TYPE.isAssignableFrom(primitiveClass))
      return Double.class.isAssignableFrom(parameterClass);
    else if (Float.TYPE.isAssignableFrom(primitiveClass))
      return Float.class.isAssignableFrom(parameterClass);
    else if (Long.TYPE.isAssignableFrom(primitiveClass))
      return Long.class.isAssignableFrom(parameterClass);
    else if (Short.TYPE.isAssignableFrom(primitiveClass))
      return Short.class.isAssignableFrom(parameterClass);
    else if (Byte.TYPE.isAssignableFrom(primitiveClass))
      return Byte.class.isAssignableFrom(parameterClass);
    return false;
  }

  @SuppressWarnings({ "rawtypes", "unchecked" })
  protected void initDocument(Class iClass, Object iInstance, ODocument iDocument, ODatabaseObject db)
      throws IllegalArgumentException, IllegalAccessException {
    for (Class currentClass = iClass; currentClass != Object.class;) {
      for (Field f : currentClass.getDeclaredFields()) {
        if (f.getName().equals("this$0"))
          continue;
        if (!f.isAccessible()) {
          f.setAccessible(true);
        }
        Object o = f.get(iInstance);
        if (o != null) {
          if (OObjectEntitySerializer.isSerializedType(f)) {
            if (o instanceof List) {
              List list = new ArrayList();
              iDocument.field(f.getName(), list);
              o = new OObjectCustomSerializerList(OObjectEntitySerializer.getSerializedType(f), iDocument, list, (List) o);
              f.set(iInstance, o);
            } else if (o instanceof Set) {
              Set set = new HashSet();
              iDocument.field(f.getName(), set);
              o = new OObjectCustomSerializerSet(OObjectEntitySerializer.getSerializedType(f), iDocument, set, (Set) o);
              f.set(iInstance, o);
            } else if (o instanceof Map) {
              Map map = new HashMap();
              iDocument.field(f.getName(), map);
              o = new OObjectCustomSerializerMap(OObjectEntitySerializer.getSerializedType(f), iDocument, map, (Map) o);
              f.set(iInstance, o);
            } else {
              o = OObjectEntitySerializer.serializeFieldValue(o.getClass(), o);
              iDocument.field(f.getName(), o);
            }
          } else {
            iDocument.field(f.getName(), OObjectEntitySerializer.typeToStream(o, OType.getTypeByClass(f.getType()), db, iDocument));
          }
        }
      }
      currentClass = currentClass.getSuperclass();
    }
  }

  protected  T createInstanceNoParameters(Class iProxiedClass, Class iOriginalClass) throws SecurityException,
      NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
    T instanceToReturn = null;
    final Class enclosingClass = iOriginalClass.getEnclosingClass();

    if (enclosingClass != null) {
      Object instanceOfEnclosingClass = createInstanceNoParameters(enclosingClass, enclosingClass);

      Constructor ctor = iProxiedClass.getConstructor(enclosingClass);

      if (ctor != null) {
        instanceToReturn = ctor.newInstance(instanceOfEnclosingClass);
      }
    } else {
      try {
        instanceToReturn = iProxiedClass.newInstance();
      } catch (InstantiationException e) {
        OLogManager.instance().error(this, "Cannot create an instance of the enclosing class '%s'", iOriginalClass);
        throw e;
      }
    }

    return instanceToReturn;

  }

  protected  T createInstanceNoParameters(Class iProxiedClass, Object iEnclosingInstance) throws SecurityException,
      NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
    T instanceToReturn = null;
    final Class enclosingClass = iEnclosingInstance.getClass();

    if (enclosingClass != null) {

      Constructor ctor = iProxiedClass.getConstructor(enclosingClass);

      if (ctor != null) {
        instanceToReturn = ctor.newInstance(iEnclosingInstance);
      }
    } else {
      instanceToReturn = iProxiedClass.newInstance();
    }

    return instanceToReturn;

  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy