Package com.adaptrex.core.ext.data

Source Code of com.adaptrex.core.ext.data.ModelInstance

/*
* Copyright 2012 Adaptrex, LLC
*
* 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.adaptrex.core.ext.data;

import com.adaptrex.core.persistence.AdaptrexPersistence;
import com.adaptrex.core.persistence.AdaptrexSession;
import com.adaptrex.core.utilities.StringUtilities;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
* ModelInstance is basically a wrapper around a map that contains the data
* for a specific instance of a database record.  This can be deeply nested
* and include ManyToOne and OneToMany relationships.
*
* This class is responsible for building a map in an Ext friendly format and
* relies heavily on the ORM implementation to retrieve the data used to build
* the map
*/
public class ModelInstance {

  private static Logger log = LoggerFactory.getLogger(ModelInstance.class);

  /*
   * Config contains all of the information necessary to determine the data
   * we want to retrieving from the store.  It also contains some formatting
   * information about the response we want to send back to Ext.
   */
  private DataConfig config;
  private AdaptrexSession session;
  /*
   * This holds the response
   */
  private Map<String, Object> data = new HashMap<String, Object>();
  /*
   * Ext wants dates formatted like this.  Make sure we convert all dates
   * to that format.  In the future we may want this to be configurable
   * but I'm unsure what situation would require it.
   */
  private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  private static SimpleDateFormat timeFormat = new SimpleDateFormat("kk:mm:ss");

  /*
   * Create the model instance
   */
  public ModelInstance(AdaptrexSession session, DataConfig config, Object entity) {
    this.config = config;
    this.session = session;
    data = buildEntityMap(session, entity, "", null);
  }

  /*
   * This generates a map for each entity in the tree.  It recurses on itself
   * to generate the map for all associated objects.
   *
   * In order to keep track of where we are in the tree, we use entityNamePath.
   * This is used to determine if certain fields should be included/excluded
   * or if we've got a join to a deeper foreign entity.
   *
   * parentEntity is used to make sure we don't attempt to associate the current
   * entity with it's own parent creating an infinte recursion
   */
  private Map<String, Object> buildEntityMap(AdaptrexSession session, Object entity, String parentEntityPath, Object parentEntity) {
    AdaptrexPersistence persistence = session.getPersistence();

    Map<String, Object> entityMap = new HashMap<String, Object>();
    if (entity == null) {
      return entityMap;
    }

    Class<?> entityClazz = entity.getClass();

    boolean isRoot = parentEntityPath.equals("");
    String entityPath = parentEntityPath + entity.getClass().getSimpleName();


    /*
     * In order to determine whether or not a field on this entity should be
     * included, we need to determine whether it has been explicitly
     * included or excluded, or if it's a foreign association, we need to know
     * if it's been explicitly included.
     *
     * The config object contains settings for the entire tree.  We want to make
     * sure we're only looking at the settings for this node (entity)
     */
    List<String> entityIncludes = new ArrayList<String>();
    List<String> entityExcludes = new ArrayList<String>();
    List<String> entityJoins = new ArrayList<String>();

    if (isRoot) {
      /*
       * Loop each include/exclude/join config and determine if they
       * are set for the root entity.
       */
      for (String incl : this.config.getIncludes()) {
        if (!incl.contains(".")) {
          entityIncludes.add(incl);
        }
      }
      for (String excl : this.config.getExcludes()) {
        if (!excl.contains(".")) {
          entityExcludes.add(excl);
        }
      }
      for (String join : this.config.getAssociationEntityNames()) {
        if (!join.contains(".")) {
          entityJoins.add(join);
        }
      }
    } else {
      /*
       * Loop each include/exclude/join config and determine if they are set for
       * the current entity at this position in the tree.  If the full path to a
       * specific field on this entity is set, we want to include it in the entity
       * specific config.  We only need the field portion when testing against
       * the current entity so that gets split out before adding to the list
       */
      for (String incl : this.config.getIncludes()) {
        if (incl.contains(entityPath + ".")) {
          entityIncludes.add(incl.split("\\.")[1]);
        }
      }

      for (String excl : this.config.getExcludes()) {
        if (excl.contains(entityPath + ".")) {
          entityExcludes.add(excl.split("\\.")[1]);
        }
      }

      for (String join : this.config.getAssociationEntityNames()) {
        if (join.contains(entityPath + ".")) {
          entityJoins.add(join = join.split("\\.")[1]);
        }
      }
    }


    /*
     * Loop through fields on our entity and determine if they should be added
     */
    for (Field field : entityClazz.getDeclaredFields()) {
      /*
       * Static fields don't get returned (reveng may add static fields
       * to the class)
       */
      if (Modifier.isStatic(field.getModifiers())) {
        continue;
      }

      /*
       * Get the field name
       */
      String fieldName = field.getName();

      /*
       * Confirm we should be including this field for this entity.  ID fields
       * get added regardless of whether they are explicitly included.
       *
       */
      if (!persistence.isIdField(entityClazz, fieldName)) {
        if (this.config.getIncludes().size() > 0) {
          if (entityIncludes.isEmpty()
              || (!entityIncludes.contains("*") && !entityIncludes.contains(fieldName))) {
            continue;
          }
        }

        if (this.config.getIncludes().size() > 0) {
          if (entityExcludes.contains("*") || entityExcludes.contains(fieldName)) {
            continue;
          }
        }
      }

      /*
       * Get the field value and make sure it's not this entities parent (prevent infinite recursion)
       */
      Object fieldValue = persistence.getFieldValue(entity, fieldName);
      if (!isRoot && fieldValue != null && fieldValue.equals(parentEntity)) {
        continue;
      }


      /*
       * Process one to many
       */
      if (persistence.isOneToMany(entityClazz, fieldName)) {
        if (!entityJoins.contains(StringUtilities.capitalize(fieldName))) {
          continue;
        }

        try {
          List<Map<String, Object>> associatedData = new ArrayList<Map<String, Object>>();

          @SuppressWarnings("unchecked")
          List<Object> assocObjList = new ArrayList<Object>((Collection<? extends Object>) fieldValue);
          for (Object assocObj : assocObjList) {
            /*
             * Don't loop back and add our parent entity (prevent infinite recursion)
             */
            if (parentEntity != null && assocObj.equals(parentEntity)) {
              continue;
            }

            associatedData.add(buildEntityMap(session, assocObj, entityPath, entity));
          }

          entityMap.put(fieldName, associatedData);

        } catch (Exception e) {
          log.warn("Error", e);
        }
        continue;
      }


      /*
       * Process many to one
       */
      if (persistence.isManyToOne(entityClazz, fieldName)) {
        if (!entityJoins.contains(StringUtilities.capitalize(fieldName))) {
          continue;
        }
        if (fieldValue == null) {
          entityMap.put(fieldName, null);
          continue;
        }

        try {
          entityMap.put(fieldName, buildEntityMap(session, fieldValue, entityPath, entity));

          /*
           * We also want the ID of our nested store... Ext should set this
           * automatically I think.  TODO: Need to customize "id" field
           */
          //Method idGetter = fieldValue.getClass().getMethod("getId");
          //entityMap.put(fieldName + "Id", idGetter.invoke(fieldValue));
        } catch (Exception e) {
          log.warn("Error", e);
        }

        continue;
      }



      /*
       * Process standard fields
       */
      try {
        if (fieldValue == null) {
          entityMap.put(fieldName, null);
          continue;
        }

        String fieldType = persistence.getFieldType(entityClazz, fieldName);

        if (fieldType.equals("date")) {
          fieldValue = dateFormat.format(fieldValue);
        } else if (fieldType.equals("time")) {
          fieldValue = timeFormat.format(fieldValue);
        }

        entityMap.put(fieldName, fieldValue);

      } catch (Exception e) {
        log.warn("Error", e);
        continue;
      }
    }

    return entityMap;
  }

  public Map<String, Object> getData() {
    return data;
  }
}
TOP

Related Classes of com.adaptrex.core.ext.data.ModelInstance

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.