SchemaClass.java :  » Web-Framework » roma-webwizard » org » romaframework » core » schema » Java Open Source

Java Open Source » Web Framework » roma webwizard 
roma webwizard » org » romaframework » core » schema » SchemaClass.java
/*
 * Copyright 2006 Luca Garulli (luca.garulli@assetdata.it)
 *
 * 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.romaframework.core.schema;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.romaframework.aspect.core.CoreAspect;
import org.romaframework.aspect.core.feature.CoreClassFeatures;
import org.romaframework.aspect.view.ViewAspect;
import org.romaframework.aspect.view.feature.ViewClassFeatures;
import org.romaframework.core.Utility;
import org.romaframework.core.aspect.Aspect;
import org.romaframework.core.aspect.AspectManager;
import org.romaframework.core.config.RomaApplicationContext;
import org.romaframework.core.entity.ComposedEntity;
import org.romaframework.core.entity.ComposedEntityInstance;
import org.romaframework.core.exception.ConfigurationNotFoundException;
import org.romaframework.core.flow.ObjectContext;
import org.romaframework.core.schema.config.FileSystemSchemaConfiguration;
import org.romaframework.core.schema.config.SchemaConfiguration;
import org.romaframework.xml.config.XmlConfigActionType;
import org.romaframework.xml.config.XmlConfigClassType;
import org.romaframework.xml.config.XmlConfigFieldType;

/**
 * Represent a class. It's not necessary that a Java class exist in the Classpath since you can define a SchemaClass that inherits
 * another Java Class and use XML descriptor to customize it. This feature avoid the writing of empty class that simply inherit real
 * domain class.
 * 
 * @author Luca Garulli (luca.garulli@assetdata.it)
 */
public class SchemaClass extends SchemaClassDefinition {

  private String                name;
  private SchemaClass            parent;
  private Class<?>              clazz;
  private Class<?>              baseClass;
  private SchemaConfiguration    descriptor;
  private SchemaManager          schemaManager;
  private boolean                reloadingStatus      = false;
  private Set<SchemaClass>      subClasses;

  private static Log            log                  = LogFactory.getLog(SchemaClass.class);

  private static final String    GET_METHOD          = "get";
  private static final String    IS_METHOD            = "is";
  private static final String    SET_METHOD          = "set";
  private static final String    ON_METHOD            = "on";
  public static final String[]  IGNORE_METHOD_NAMES  = { "toString", "hashCode", "validate", "getClass", "on*", "clone" };

  public SchemaClass(SchemaManager iEntityManager, String iEntityName, Class<?> iClass, Class<?> iBaseClass,
      SchemaConfiguration iDescriptor) throws ConfigurationNotFoundException {
    if (iClass == null && iBaseClass == null)
      // ERROR: CANNOT ASSOCIATE A JAVA CLASS
      throw new ConfigurationNotFoundException("Class " + iEntityName);

    schemaManager = iEntityManager;
    clazz = iClass;
    name = iEntityName;
    baseClass = iBaseClass;

    if (iDescriptor != null)
      // USE THE DESCRIPTOR AS PARAMETER (DEFINED AS INLINE)
      descriptor = iDescriptor;
    else {
      descriptor = RomaApplicationContext.getInstance().getBean(SchemaConfigurationLoader.class)
          .getXmlFileSystemSchemaConfiguration(name);

      if (descriptor != null)
        ObjectContext.getInstance().getComponent(SchemaReloader.class).addResourceForReloading(
            ((FileSystemSchemaConfiguration) descriptor).getFile(), iEntityName);
    }

    File classFile = RomaApplicationContext.getInstance().getBean(SchemaClassResolver.class).getFileOwner(
        name + SchemaClassResolver.CLASS_SUFFIX);

    if (classFile != null)
      ObjectContext.getInstance().getComponent(SchemaReloader.class).addResourceForReloading(classFile, iEntityName);
  }

  @Override
  public boolean equals(Object arg0) {
    if (arg0 == null || !(arg0 instanceof SchemaClass))
      return false;

    SchemaClass other = (SchemaClass) arg0;

    if (name != null)
      return name.equals(other.getName());
    return false;
  }

  @Override
  public int hashCode() {
    if (name != null)
      return name.hashCode();
    return -1;
  }

  public void configure() {
    config();
  }

  private void config() {
    inheritBySuperClass();
    readAllAnnotations();

    readFields();
    readActions();
    readEvents();
  }

  /**
   * Reload class configuration from file. This event is invoked when the file descriptor is changed.
   */
  public void signalUpdatedFile(File iFile) {
    try {
      log.warn("[SchemaClass.signalUpdatedFile] Reloading configuration for class:  '" + name + "' from file: " + iFile);
      if (subClasses != null) {
        // IF ANY, RELOAD ALL SUB-CLASSES IN CASCADING
        for (SchemaClass cls : subClasses)
          cls.signalUpdatedFile(null);
      }

      // REATTACH THE NEW CLASS LOADED
      // Class<?> oldClass = clazz;
      // clazz = ObjectContext.getInstance().getComponent(SchemaClassResolver.class).reloadEntityClass(clazz.getName());
      //
      // updateAllReferencesToOldClass(oldClass);

      // RESET CLASS INFO
      reset();

      reloadingStatus = true;
      if (iFile != null && iFile.getName().endsWith(SchemaClassResolver.DESCRIPTOR_SUFFIX))
        descriptor.load();
      config();
      reloadingStatus = false;
    } catch (Exception e) {
      log.error("[SchemaClass.signalUpdatedFile] Error", e);
    }
  }

  private void updateAllReferencesToOldClass(Class<?> oldClass) {
    int counter = 0;
    for (SchemaClass schema : schemaManager.getAllClassesInfo()) {
      Iterator<SchemaField> it = schema.getFieldIterator();
      SchemaField f;
      while (it.hasNext()) {
        f = it.next();
        if (f.getTypeClass() != null && f.getTypeClass().equals(oldClass)) {
          f.setTypeClass(clazz);
          ++counter;
        }
      }
    }
    log.warn("[SchemaClass.updateAllReferencesToOldClass] Total references updated: " + counter);
  }

  private void reset() {
    allFeatures.clear();
    fields.clear();
    orderedFields.clear();
    actions.clear();
    orderedActions.clear();
    events.clear();
  }

  public Set<SchemaClass> getSubClasses() {
    return subClasses;
  }

  public Class<?> getClazz() {
    return clazz != null ? clazz : baseClass;
  }

  public String getName() {
    return name;
  }

  public SchemaClass getParent() {
    return parent;
  }

  public SchemaConfiguration getDescriptor() {
    return descriptor;
  }

  @Override
  public SchemaClass getSchemaClass() {
    return this;
  }

  public SchemaManager getSchemaManager() {
    return schemaManager;
  }

  private void inheritBySuperClass() {
    Class<?> superClass = null;

    if (clazz != null)
      // GET SUPER CLASS IF ANY
      superClass = clazz.getSuperclass();
    else
      // NO CONCRETE JAVA CLASS FOUND: COPY FROM BASE CLASS
      superClass = baseClass;

    parent = null;
    if (superClass != null && superClass != Object.class)
      parent = schemaManager.getClassInfo(superClass, null);

    if (parent != null) {
      if (!reloadingStatus)
        // ADD MYSELF AS SUB-CLASS OF MY SUPER CLASS
        parent.addSubclass(this);
      copyDefinition(parent);
    }
  }

  /**
   * Add subclass to determine inheritance three. Useful on reloading to refresh all entity in cascading.
   * 
   * @param iSubClassEntity
   *          SubClass Entity
   */
  private void addSubclass(SchemaClass iSubClassEntity) {
    if (subClasses == null)
      subClasses = new HashSet<SchemaClass>();

    subClasses.add(iSubClassEntity);
  }

  private void readAllAnnotations() {
    // BROWSE ALL ASPECTS
    Collection<Aspect> aspects = AspectManager.getInstance().getConfigurationValues();

    String annotationName;
    Class<? extends Annotation> annotationClass;
    Annotation annotation;

    XmlConfigClassType parentDescriptor = null;

    if (descriptor != null)
      parentDescriptor = descriptor.getType();

    for (Aspect aspect : aspects) {
      // READ CLASS ANNOTATIONS

      if (clazz != null) {
        // COMPOSE ANNOTATION NAME BY ASPECT
        annotationName = aspect.aspectName();
        annotationName = Character.toUpperCase(annotationName.charAt(0)) + annotationName.substring(1) + "Class";

        // CHECK FOR ANNOTATION PRESENCE
        try {
          annotationClass = (Class<? extends Annotation>) Class.forName(Utility.ROMA_PACKAGE + ".aspect." + aspect.aspectName()
              + ".annotation." + annotationName);
          annotation = clazz.getAnnotation(annotationClass);
        } catch (ClassNotFoundException e) {
          // ANNOTATION CLASS NOT EXIST FOR CURRENT ASPECT
          annotation = null;
        }
      } else
        annotation = null;

      // READ XML ANNOTATIONS
      aspect.configClass(this, annotation, parentDescriptor);
    }
  }

  private void readFields() {
    readFields(clazz != null ? clazz : baseClass);
  }

  private void readFields(Class<?> iClass) {
    SchemaField fieldInfo;
    Method[] methodArray = SchemaHelper.getMethods(iClass);
    Method getterMethod;
    Method setterMethod;
    Field field;
    String fieldName;
    Class<?> fieldType;
    for (int i = 0; i < methodArray.length; ++i) {
      getterMethod = methodArray[i];

      if (Modifier.isStatic(getterMethod.getModifiers()))
        // JUMP STATIC FIELDS
        continue;

      if (!Modifier.isPublic(getterMethod.getModifiers()))
        // JUMP NOT PUBLIC FIELDS
        continue;

      fieldName = getterMethod.getName();

      int prefixLength;
      if (fieldName.startsWith(GET_METHOD))
        prefixLength = GET_METHOD.length();
      else if (fieldName.startsWith(IS_METHOD))
        prefixLength = IS_METHOD.length();
      else
        continue;

      if (getterMethod.getParameterTypes() != null && getterMethod.getParameterTypes().length > 0)
        continue;

      if (isToIgnoreMethod(getterMethod))
        // IGNORE THE METHOD SINCE IT'S IN IGNORE_METHOD_NAMES
        continue;

      if (fieldName.length() <= prefixLength)
        // GET METHOD ONLY: JUMP IT
        continue;

      log.debug("[SchemaClass] Class " + getName() + " found field: " + fieldName);

      // GET FIELD NAME
      fieldName = Character.toLowerCase(fieldName.charAt(prefixLength)) + fieldName.substring(prefixLength + 1);

      // GET FIELD TYPE
      fieldType = getterMethod.getReturnType();

      try {
        // TRY TO FIND SETTER METHOD IF ANY
        setterMethod = iClass.getMethod(SET_METHOD + getterMethod.getName().substring(prefixLength), new Class[] { fieldType });
      } catch (Exception e) {
        setterMethod = null;
      }

      // TRY TO FIND FIELD IF ANY
      field = SchemaHelper.getField(iClass, fieldName);
      if (field != null && field.getType() != Object.class) {
        fieldType = field.getType();
      }

      if (getterMethod.getName().equals("getEntity") && ComposedEntity.class.isAssignableFrom(getterMethod.getDeclaringClass())) {
        // ENTITY FIELD: CHECK FOR SPECIAL ENTITY TYPE
        // TODO: REMOVE THIS WIRED CONCEPT
        Class<?> entityClass = (Class<?>) getFeatures(ViewAspect.ASPECT_NAME).getAttribute(ViewClassFeatures.ENTITY);
        if (entityClass != null && entityClass != Object.class)
          fieldType = entityClass;
        else {
          if (!iClass.equals(ComposedEntityInstance.class))
            log
                .warn("[SchemaClass.readFields] Cannot find the definition of annotation @ViewClass(entity=X.class) for class '"
                    + iClass
                    + "'. Since it's a ComposedEntityInstance implementation an annotation is expected to expand the entity correctly.");
        }
      }

      fieldInfo = getField(fieldName);
      if (fieldInfo == null) {
        // FIELD NOT EXISTENT: CREATE IT AND INSERT IN THE COLLECTION
        fieldInfo = new SchemaField(this);
        setField(fieldName, fieldInfo);
      }

      // GENERATE OR OVERWRITE (IN CASE OF INHERITANCE) FIELD CONFIGURATION
      fieldInfo.configure(RomaApplicationContext.getInstance().getBean(ViewAspect.class), fieldName, fieldType, field,
          getterMethod, setterMethod);

      fieldInfo.setOrder(getFieldOrder(fieldInfo));
    }

    Collections.sort(orderedFields);
  }

  private void readActions() {
    if (clazz == null)
      // NO CONCRETE JAVA CLASS FOUND
      return;

    SchemaAction actionInfo;
    Method[] methodArray = SchemaHelper.getMethods(clazz);
    Method currentMethod;
    String methodName;
    for (int i = 0; i < methodArray.length; ++i) {
      currentMethod = methodArray[i];
      methodName = currentMethod.getName();

      log.debug("[SchemaClass] TEMP Class " + getName() + " found method: " + currentMethod);

      if (Modifier.isStatic(currentMethod.getModifiers()))
        // JUMP STATIC METHODS
        continue;

      if (!Modifier.isPublic(currentMethod.getModifiers()))
        // IGNORE NON PUBLIC METHODS
        continue;

      if (currentMethod.getParameterTypes().length > 0)
        // IGNORE METHODS WITH PARAMETERS
        continue;

      if (isToIgnoreMethod(currentMethod))
        // IGNORE METHOD
        continue;

      if (methodName.startsWith(GET_METHOD) || methodName.startsWith(IS_METHOD) || methodName.startsWith(SET_METHOD))
        // GETTER OR SETTER: IGNORE IT (ARE TREATED AS FIELDS)
        continue;

      log.debug("[SchemaClass] Class " + getName() + " found method: " + methodName);

      actionInfo = getAction(methodName);

      if (actionInfo == null) {
        // ACTION NOT EXISTENT: CREATE IT AND INSERT IN THE COLLECTION
        actionInfo = new SchemaAction(this);
        setAction(methodName, actionInfo);
      }

      // GENERATE OR OVERWRITE (IN CASE OF INHERITANCE) ACTION CONFIGURATION
      actionInfo.configure(currentMethod);

      actionInfo.setOrder(getActionOrder(actionInfo));
    }

    Collections.sort(orderedActions);
  }

  private void readEvents() {
    if (clazz == null)
      // NO CONCRETE JAVA CLASS FOUND
      return;

    SchemaEvent eventInfo;
    Method[] methodArray = SchemaHelper.getMethods(clazz);
    Method eventMethod;
    String eventName;
    for (int i = 0; i < methodArray.length; ++i) {
      eventMethod = methodArray[i];

      if (Modifier.isStatic(eventMethod.getModifiers()))
        // JUMP STATIC FIELDS
        continue;

      if (!Modifier.isPublic(eventMethod.getModifiers()))
        // JUMP NOT PUBLIC FIELDS
        continue;

      if (!eventMethod.getName().startsWith(ON_METHOD))
        continue;

      // GET FIELD NAME
      eventName = Character.toLowerCase(eventMethod.getName().charAt(ON_METHOD.length()))
          + eventMethod.getName().substring(ON_METHOD.length() + 1);

      eventInfo = getEvent(eventName);

      if (eventInfo == null) {
        // EVENT NOT EXISTENT: CREATE IT AND INSERT IN THE COLLECTION
        eventInfo = new SchemaEvent(this, eventName);
        setEvent(eventName, eventInfo);
      }

      // GENERATE OR OVERWRITE (IN CASE OF INHERITANCE) EVENT CONFIGURATION
      eventInfo.configure(eventMethod);
    }
  }

  @Override
  public String toString() {
    return name + (clazz != null ? " (class:" + clazz.getName() + ")" : "");
  }

  private short getFieldOrder(SchemaField iField) {
    String orderedValues = (String) getFeature(CoreAspect.ASPECT_NAME, CoreClassFeatures.ORDER_FIELDS);

    if (orderedValues != null) {
      StringTokenizer tokenizer = new StringTokenizer(orderedValues, " ");
      for (short fieldNum = 0; tokenizer.hasMoreTokens(); ++fieldNum) {
        if (tokenizer.nextToken().equals(iField.getName()))
          // FOUND: RETURN THE ORDER
          return fieldNum;
      }
    }

    if (descriptor != null && descriptor.getType() != null && descriptor.getType().getFields() != null) {
      // SEARCH FORM DEFINITION IN DESCRIPTOR
      XmlConfigFieldType[] allFields = descriptor.getType().getFields().getFieldArray();
      for (short fieldNum = 0; fieldNum < allFields.length; ++fieldNum) {
        if (allFields[fieldNum].getName().equals(iField.getName())) {
          // FOUND: RETURN THE ORDER
          return fieldNum;
        }
      }
    }

    return iField.getOrder();
  }

  private short getActionOrder(SchemaElement iAction) {
    String orderedValues = (String) getFeature(CoreAspect.ASPECT_NAME, CoreClassFeatures.ORDER_ACTIONS);

    if (orderedValues != null) {
      StringTokenizer tokenizer = new StringTokenizer(orderedValues, " ");
      for (short actionNum = 0; tokenizer.hasMoreTokens(); ++actionNum) {
        if (tokenizer.nextToken().equals(iAction.getName()))
          // FOUND: RETURN THE ORDER
          return actionNum;
      }
    }

    if (descriptor != null && descriptor.getType() != null && descriptor.getType().getActions() != null) {
      // SEARCH FORM DEFINITION IN DESCRIPTOR
      XmlConfigActionType[] allActions = descriptor.getType().getActions().getActionArray();
      for (short actionNum = 0; actionNum < allActions.length; ++actionNum) {
        if (allActions[actionNum].getName().equals(iAction.getName())) {
          // FOUND: RETURN THE ORDER
          return actionNum;
        }
      }
    }
    return iAction.getOrder();
  }

  private boolean isToIgnoreMethod(Method currentMethod) {
    String methodName = currentMethod.getName();
    // CHECK FOR FIXED NAMES TO IGNORE
    for (int ignoreId = 0; ignoreId < IGNORE_METHOD_NAMES.length; ++ignoreId) {
      if (SchemaAction.ignoreMethod(IGNORE_METHOD_NAMES[ignoreId], methodName))
        return true;
    }

    // CHECK FOR CUSTOM NAMES TO IGNORE, IF ANY
    for (Iterator<String> it = schemaManager.getIgnoreActions().iterator(); it.hasNext();) {
      if (SchemaAction.ignoreMethod(it.next(), methodName))
        return true;
    }

    return false;
  }

  public Class<?> getBaseClass() {
    return baseClass;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.