Android Open Source - LitePal Lite Pal Base






From Project

Back to project page LitePal.

License

The source code is released under:

Apache License

If you think the Android project LitePal listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C)  Tony Green, Litepal Framework Open Source Project
 */*from  w w  w .  ja  v a 2 s. c om*/
 * 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.litepal;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.litepal.crud.model.AssociationsInfo;
import org.litepal.exceptions.DatabaseGenerateException;
import org.litepal.parser.LitePalAttr;
import org.litepal.tablemanager.model.AssociationsModel;
import org.litepal.tablemanager.model.TableModel;
import org.litepal.tablemanager.typechange.BooleanOrm;
import org.litepal.tablemanager.typechange.DateOrm;
import org.litepal.tablemanager.typechange.DecimalOrm;
import org.litepal.tablemanager.typechange.NumericOrm;
import org.litepal.tablemanager.typechange.OrmChange;
import org.litepal.tablemanager.typechange.TextOrm;
import org.litepal.util.BaseUtility;
import org.litepal.util.Const;
import org.litepal.util.DBUtility;

/**
 * Base class of all the LitePal components. If each component need to
 * interactive with other components or they have some same logic with duplicate
 * codes, LitePalBase may be the solution.
 * 
 * @author Tony Green
 * @since 1.1
 */
public abstract class LitePalBase {

  public static final String TAG = "LitePalBase";

  /**
   * Action to get associations.
   */
  private static final int GET_ASSOCIATIONS_ACTION = 1;

  /**
   * Action to get association info.
   */
  private static final int GET_ASSOCIATION_INFO_ACTION = 2;

  /**
   * All the supporting mapping types currently in the array.
   */
  private OrmChange[] typeChangeRules = { new NumericOrm(), new TextOrm(), new BooleanOrm(),
      new DecimalOrm(), new DateOrm() };

  /**
   * The collection contains all association models.
   */
  private Collection<AssociationsModel> mAssociationModels;

  /**
   * The collection contains all association info.
   */
  private Collection<AssociationsInfo> mAssociationInfos;

  /**
   * This method is used to get the table model by the class name passed
   * in.The principle to generate table model is that each field in the class
   * with private modifier and has a type among int/Integer, long/Long,
   * short/Short, float/Float, double/Double, char/Character, boolean/Boolean
   * or String, would generate a column with same name as corresponding field.
   * If users don't want some of the fields map a column, declare them as
   * protected or default.
   * 
   * @param className
   *            The full name of the class to map in database.
   * @return A table model with table name, class name and the map of column
   *         name and column type.
   */
  protected TableModel getTableModel(String className) {
    String tableName = DBUtility.getTableNameByClassName(className);
    TableModel tableModel = new TableModel();
    tableModel.setTableName(tableName);
    tableModel.setClassName(className);
    List<Field> supportedFields = getSupportedFields(className);
    for (Field field : supportedFields) {
      String fieldName = field.getName();
      Class<?> fieldTypeClass = field.getType();
      String fieldType = fieldTypeClass.getName();
      String columnName = null;
      String columnType = null;
      for (OrmChange ormChange : typeChangeRules) {
        String[] relations = ormChange.object2Relation(className, fieldName, fieldType);
        if (relations != null) {
          columnName = relations[0];
          columnType = relations[1];
          tableModel.addColumn(columnName, columnType);
          break;
        }
      }
    }
    return tableModel;
  }

  /**
   * This method is used to get association models depends on the given class
   * name list.
   * 
   * @param classNames
   *            The names of the classes that want to get their associations.
   * @return Collection of association models.
   */
  protected Collection<AssociationsModel> getAssociations(List<String> classNames) {
    if (mAssociationModels == null) {
      mAssociationModels = new HashSet<AssociationsModel>();
    }
    mAssociationModels.clear();
    for (String className : classNames) {
      analyzeClassFields(className, GET_ASSOCIATIONS_ACTION);
    }
    return mAssociationModels;
  }

  /**
   * Get the association info model by the class name.
   * 
   * @param className
   *            The class name to introspection.
   * @return Collection of association info.
   */
  protected Collection<AssociationsInfo> getAssociationInfo(String className) {
    if (mAssociationInfos == null) {
      mAssociationInfos = new HashSet<AssociationsInfo>();
    }
    mAssociationInfos.clear();
    analyzeClassFields(className, GET_ASSOCIATION_INFO_ACTION);
    return mAssociationInfos;
  }

  /**
   * Find all the fields in the class. But not each field is supported to add
   * a column to the table. Only the basic data types and String are
   * supported. This method will intercept all the types which are not
   * supported and return a new list of supported fields.
   * 
   * @param className
   *            The full name of the class.
   * @return A list of supported fields
   */
  protected List<Field> getSupportedFields(String className) {
    List<Field> supportedFields = new ArrayList<Field>();
    Class<?> dynamicClass = null;
    try {
      dynamicClass = Class.forName(className);
    } catch (ClassNotFoundException e) {
      throw new DatabaseGenerateException(DatabaseGenerateException.CLASS_NOT_FOUND + className);
    }
    Field[] fields = dynamicClass.getDeclaredFields();
    for (Field field : fields) {
      int modifiers = field.getModifiers();
      if (Modifier.isPrivate(modifiers) && !Modifier.isStatic(modifiers)) {
        Class<?> fieldTypeClass = field.getType();
        String fieldType = fieldTypeClass.getName();
        if (BaseUtility.isFieldTypeSupported(fieldType)) {
          supportedFields.add(field);
        }
      }
    }
    return supportedFields;
  }

  /**
   * If the field type implements from List or Set, regard it as a collection.
   * 
   * @param fieldType
   *            The field type.
   * @return True if the field type is collection, false otherwise.
   */
  protected boolean isCollection(Class<?> fieldType) {
    return isList(fieldType) || isSet(fieldType);
  }

  /**
   * If the field type implements from List, regard it as a list.
   * 
   * @param fieldType
   *            The field type.
   * @return True if the field type is List, false otherwise.
   */
  protected boolean isList(Class<?> fieldType) {
    return List.class.isAssignableFrom(fieldType);
  }

  /**
   * If the field type implements from Set, regard it as a set.
   * 
   * @param fieldType
   *            The field type.
   * @return True if the field type is Set, false otherwise.
   */
  protected boolean isSet(Class<?> fieldType) {
    return Set.class.isAssignableFrom(fieldType);
  }

  /**
   * Judge the passed in column is an id column or not. The column named id or
   * _id will be considered as id column.
   * 
   * @param columnName
   *            The name of column.
   * @return Return true if it's id column, otherwise return false.
   */
  protected boolean isIdColumn(String columnName) {
    return "_id".equalsIgnoreCase(columnName) || "id".equalsIgnoreCase(columnName);
  }

  /**
   * If two tables are associated, one table have a foreign key column. The
   * foreign key column name will be the associated table name with _id
   * appended.
   * 
   * @param associatedTableName
   *            The associated table name.
   * @return The foreign key column name.
   */
  protected String getForeignKeyColumnName(String associatedTableName) {
    return BaseUtility.changeCase(associatedTableName + "_id");
  }

  /**
   * Analyze the two parameters passed in. Return the first one of the two
   * class names in alphabetical order as the one who holds foreign key. This
   * is only going to work under one2one bidirectional association. When it's
   * one2one unidirectional association, the foreign key column will be always
   * on the side of the class which declares the association.
   * 
   * @param className
   *            The first class name.
   * @param associatedClassName
   *            The second class class.
   * @return The first one of the two passed in parameters in alphabetical
   *         order.
   */
  @Deprecated
  protected String whoHoldsForeignKey(String className, String associatedClassName) {
    String tableName = DBUtility.getTableNameByClassName(className);
    String associatedTableName = DBUtility.getTableNameByClassName(associatedClassName);
    if (tableName.compareTo(associatedTableName) < 0) {
      return className;
    } else {
      return associatedClassName;
    }
  }

  /**
   * Introspection of the passed in class. Analyze the fields of current class
   * and find out the associations of it.
   * 
   * @param className
   *            The class name to introspection.
   * @param action
   *            Between {@link LitePalBase#GET_ASSOCIATIONS_ACTION} and
   *            {@link LitePalBase#GET_ASSOCIATION_INFO_ACTION}
   */
  private void analyzeClassFields(String className, int action) {
    Class<?> dynamicClass = null;
    try {
      dynamicClass = Class.forName(className);
      Field[] fields = dynamicClass.getDeclaredFields();
      for (Field field : fields) {
        if (isPrivateAndNonPrimitive(field)) {
          oneToAnyConditions(className, field, action);
          manyToAnyConditions(className, field, action);
        }
      }
    } catch (ClassNotFoundException ex) {
      ex.printStackTrace();
      throw new DatabaseGenerateException(DatabaseGenerateException.CLASS_NOT_FOUND + className);
    }
  }

  /**
   * Judge the field is a private non primitive field or not.
   * 
   * @param field
   *            The field to judge.
   * @return True if the field is <b>private</b> and <b>non primitive</b>,
   *         false otherwise.
   */
  private boolean isPrivateAndNonPrimitive(Field field) {
    return Modifier.isPrivate(field.getModifiers()) && !field.getType().isPrimitive();
  }

  /**
   * Deals with one to any association conditions. e.g. Song and Album. An
   * album have many songs, and a song belongs to one album. So if there's an
   * Album model defined in Song with private modifier, and in Album there's a
   * List or Set with generic type of Song and declared as private modifier,
   * they are one2many association. If there's no List or Set defined in
   * Album, they will become one2one associations. If there's also a Song
   * model defined in Album with private modifier, maybe the album just have
   * one song, they are one2one association too.
   * 
   * When it's many2one association, it's easy to just simply add a foreign id
   * column to the many side model's table. But when it comes to many2many
   * association, it can not be done without intermediate join table in
   * database. LitePal assumes that this join table's name is the
   * concatenation of the two target table names in alphabetical order.
   * 
   * @param className
   *            Source class name.
   * @param field
   *            A field of source class.
   * @param action
   *            Between {@link LitePalBase#GET_ASSOCIATIONS_ACTION} and
   *            {@link LitePalBase#GET_ASSOCIATION_INFO_ACTION}
   * 
   * @throws ClassNotFoundException
   */
  private void oneToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {
    Class<?> fieldTypeClass = field.getType();
    // If the mapping list contains the class name
    // defined in one class.
    if (LitePalAttr.getInstance().getClassNames().contains(fieldTypeClass.getName())) {
      Class<?> reverseDynamicClass = Class.forName(fieldTypeClass.getName());
      Field[] reverseFields = reverseDynamicClass.getDeclaredFields();
      // Look up if there's a reverse association
      // definition in the reverse class.
      boolean reverseAssociations = false;
      // Begin to check the fields of the defined
      // class.
      for (int i = 0; i < reverseFields.length; i++) {
        Field reverseField = reverseFields[i];
        if (Modifier.isPrivate(reverseField.getModifiers())) {
          Class<?> reverseFieldTypeClass = reverseField.getType();
          // If there's the from class name in the
          // defined class, they are one2one bidirectional
          // associations.
          if (className.equals(reverseFieldTypeClass.getName())) {
            if (action == GET_ASSOCIATIONS_ACTION) {
              addIntoAssociationModelCollection(className, fieldTypeClass.getName(),
                  fieldTypeClass.getName(), Const.Model.ONE_TO_ONE);
            } else if (action == GET_ASSOCIATION_INFO_ACTION) {
              addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),
                  fieldTypeClass.getName(), field, reverseField, Const.Model.ONE_TO_ONE);
            }
            reverseAssociations = true;
          }
          // If there's the from class Set or List in
          // the defined class, they are many2one bidirectional
          // associations.
          else if (isCollection(reverseFieldTypeClass)) {
            String genericTypeName = getGenericTypeName(reverseField);
            if (className.equals(genericTypeName)) {
              if (action == GET_ASSOCIATIONS_ACTION) {
                addIntoAssociationModelCollection(className, fieldTypeClass.getName(),
                    className, Const.Model.MANY_TO_ONE);
              } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),
                    className, field, reverseField, Const.Model.MANY_TO_ONE);
              }
              reverseAssociations = true;
            }
          }
          // If there's no from class in the defined class, they are
          // one2one unidirectional associations.
          if ((i == reverseFields.length - 1) && !reverseAssociations) {
            if (action == GET_ASSOCIATIONS_ACTION) {
              addIntoAssociationModelCollection(className, fieldTypeClass.getName(),
                  fieldTypeClass.getName(), Const.Model.ONE_TO_ONE);
            } else if (action == GET_ASSOCIATION_INFO_ACTION) {
              addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),
                  fieldTypeClass.getName(), field, null, Const.Model.ONE_TO_ONE);
            }
          }
        }
      }
    }
  }

  /**
   * Deals with one to any association conditions. e.g. Song and Album. An
   * album have many songs, and a song belongs to one album. So if there's an
   * Album model defined in Song with private modifier, and in Album there's a
   * List or Set with generic type of Song and declared as private modifier,
   * they are one2many association. If there's no List or Set defined in
   * Album, they will become one2one associations. If there's also a Song
   * model defined in Album with private modifier, maybe the album just have
   * one song, they are one2one association too.
   * 
   * When it's many2one association, it's easy to just simply add a foreign id
   * column to the many side model's table. But when it comes to many2many
   * association, it can not be done without intermediate join table in
   * database. LitePal assumes that this join table's name is the
   * concatenation of the two target table names in alphabetical order.
   * 
   * @param className
   *            Source class name.
   * @param field
   *            A field of source class.
   * @param action
   *            Between {@link LitePalBase#GET_ASSOCIATIONS_ACTION} and
   *            {@link LitePalBase#GET_ASSOCIATION_INFO_ACTION}
   * 
   * @throws ClassNotFoundException
   */
  private void manyToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {
    if (isCollection(field.getType())) {
      String genericTypeName = getGenericTypeName(field);
      // If the mapping list contains the genericTypeName, begin to check
      // this genericTypeName class.
      if (LitePalAttr.getInstance().getClassNames().contains(genericTypeName)) {
        Class<?> reverseDynamicClass = Class.forName(genericTypeName);
        Field[] reverseFields = reverseDynamicClass.getDeclaredFields();
        // Look up if there's a reverse association
        // definition in the reverse class.
        boolean reverseAssociations = false;
        for (int i = 0; i < reverseFields.length; i++) {
          Field reverseField = reverseFields[i];
          // Only map private fields
          if (Modifier.isPrivate(reverseField.getModifiers())) {
            Class<?> reverseFieldTypeClass = reverseField.getType();
            // If there's a from class name defined in the reverse
            // class, they are many2one bidirectional
            // associations.
            if (className.equals(reverseFieldTypeClass.getName())) {
              if (action == GET_ASSOCIATIONS_ACTION) {
                addIntoAssociationModelCollection(className, genericTypeName,
                    genericTypeName, Const.Model.MANY_TO_ONE);
              } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName,
                    field, reverseField, Const.Model.MANY_TO_ONE);
              }
              reverseAssociations = true;
            }
            // If there's a List or Set contains from class name
            // defined in the reverse class, they are many2many
            // association.
            else if (isCollection(reverseFieldTypeClass)) {
              String reverseGenericTypeName = getGenericTypeName(reverseField);
              if (className.equals(reverseGenericTypeName)) {
                if (action == GET_ASSOCIATIONS_ACTION) {
                  addIntoAssociationModelCollection(className, genericTypeName, null,
                      Const.Model.MANY_TO_MANY);
                } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                  addIntoAssociationInfoCollection(className, genericTypeName, null, field,
                      reverseField, Const.Model.MANY_TO_MANY);
                }
                reverseAssociations = true;
              }
            }
            // If there's no from class in the defined class, they
            // are many2one unidirectional associations.
            if ((i == reverseFields.length - 1) && !reverseAssociations) {
              if (action == GET_ASSOCIATIONS_ACTION) {
                addIntoAssociationModelCollection(className, genericTypeName,
                    genericTypeName, Const.Model.MANY_TO_ONE);
              } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName,
                    field, null, Const.Model.MANY_TO_ONE);
              }
            }
          }
        }
      }
    }
  }

  /**
   * Package a {@link AssociationsModel}, and add it into
   * {@link #mAssociationModels} Collection.
   * 
   * @param className
   *            The class name for {@link AssociationsModel}.
   * @param associatedClassName
   *            The associated class name for {@link AssociationsModel}.
   * @param classHoldsForeignKey
   *            The class which holds foreign key.
   * @param associationType
   *            The association type for {@link AssociationsModel}.
   */
  private void addIntoAssociationModelCollection(String className, String associatedClassName,
      String classHoldsForeignKey, int associationType) {
    AssociationsModel associationModel = new AssociationsModel();
    associationModel.setTableName(DBUtility.getTableNameByClassName(className));
    associationModel.setAssociatedTableName(DBUtility.getTableNameByClassName(associatedClassName));
    associationModel.setTableHoldsForeignKey(DBUtility.getTableNameByClassName(classHoldsForeignKey));
    associationModel.setAssociationType(associationType);
    mAssociationModels.add(associationModel);
  }

  /**
   * Package a {@link AssociationsInfo}, and add it into
   * {@link #mAssociationInfos} Collection.
   * 
   * @param selfClassName
   *            The class name of self model.
   * @param associatedClassName
   *            The class name of the class which associated with self class.
   * @param classHoldsForeignKey
   *            The class which holds foreign key.
   * @param associateOtherModelFromSelf
   *            The field of self class to declare has association with other
   *            class.
   * @param associateSelfFromOtherModel
   *            The field of the associated class to declare has association
   *            with self class.
   * @param associationType
   *            The association type.
   */
  private void addIntoAssociationInfoCollection(String selfClassName, String associatedClassName,
      String classHoldsForeignKey, Field associateOtherModelFromSelf,
      Field associateSelfFromOtherModel, int associationType) {
    AssociationsInfo associationInfo = new AssociationsInfo();
    associationInfo.setSelfClassName(selfClassName);
    associationInfo.setAssociatedClassName(associatedClassName);
    associationInfo.setClassHoldsForeignKey(classHoldsForeignKey);
    associationInfo.setAssociateOtherModelFromSelf(associateOtherModelFromSelf);
    associationInfo.setAssociateSelfFromOtherModel(associateSelfFromOtherModel);
    associationInfo.setAssociationType(associationType);
    mAssociationInfos.add(associationInfo);
  }

  /**
   * Get the generic type name of List or Set. If there's no generic type of
   * List or Set return null.
   * 
   * @param field
   *            A generic type field.
   * @return The name of generic type of List of Set.
   */
  private String getGenericTypeName(Field field) {
    Type genericType = field.getGenericType();
    if (genericType != null) {
      if (genericType instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) genericType;
        Class<?> genericArg = (Class<?>) parameterizedType.getActualTypeArguments()[0];
        return genericArg.getName();
      }
    }
    return null;
  }

}




Java Source Code List

org.litepal.LitePalApplication.java
org.litepal.LitePalBase.java
org.litepal.crud.AssociationsAnalyzer.java
org.litepal.crud.ClusterQuery.java
org.litepal.crud.DataHandler.java
org.litepal.crud.DataSupport.java
org.litepal.crud.DeleteHandler.java
org.litepal.crud.DynamicExecutor.java
org.litepal.crud.Many2ManyAnalyzer.java
org.litepal.crud.Many2OneAnalyzer.java
org.litepal.crud.One2OneAnalyzer.java
org.litepal.crud.QueryHandler.java
org.litepal.crud.SaveHandler.java
org.litepal.crud.UpdateHandler.java
org.litepal.crud.model.AssociationsInfo.java
org.litepal.exceptions.DataSupportException.java
org.litepal.exceptions.DatabaseGenerateException.java
org.litepal.exceptions.GlobalException.java
org.litepal.exceptions.InvalidAttributesException.java
org.litepal.exceptions.ParseConfigurationFileException.java
org.litepal.litepalsample.activity.AggregateActivity.java
org.litepal.litepalsample.activity.AverageSampleActivity.java
org.litepal.litepalsample.activity.CRUDActivity.java
org.litepal.litepalsample.activity.CountSampleActivity.java
org.litepal.litepalsample.activity.DeleteSampleActivity.java
org.litepal.litepalsample.activity.MainActivity.java
org.litepal.litepalsample.activity.ManageTablesActivity.java
org.litepal.litepalsample.activity.MaxSampleActivity.java
org.litepal.litepalsample.activity.MinSampleActivity.java
org.litepal.litepalsample.activity.ModelListActivity.java
org.litepal.litepalsample.activity.ModelStructureActivity.java
org.litepal.litepalsample.activity.QuerySampleActivity.java
org.litepal.litepalsample.activity.SaveSampleActivity.java
org.litepal.litepalsample.activity.SumSampleActivity.java
org.litepal.litepalsample.activity.TableListActivity.java
org.litepal.litepalsample.activity.TableStructureActivity.java
org.litepal.litepalsample.activity.UpdateSampleActivity.java
org.litepal.litepalsample.adapter.DataArrayAdapter.java
org.litepal.litepalsample.adapter.StringArrayAdapter.java
org.litepal.litepalsample.model.Album.java
org.litepal.litepalsample.model.Singer.java
org.litepal.litepalsample.model.Song.java
org.litepal.litepalsample.util.Utility.java
org.litepal.model.Table_Schema.java
org.litepal.parser.LitePalAttr.java
org.litepal.parser.LitePalContentHandler.java
org.litepal.parser.LitePalParser.java
org.litepal.tablemanager.AssociationCreator.java
org.litepal.tablemanager.AssociationUpdater.java
org.litepal.tablemanager.Connector.java
org.litepal.tablemanager.Creator.java
org.litepal.tablemanager.Dropper.java
org.litepal.tablemanager.Generator.java
org.litepal.tablemanager.LitePalOpenHelper.java
org.litepal.tablemanager.Upgrader.java
org.litepal.tablemanager.model.AssociationsModel.java
org.litepal.tablemanager.model.TableModel.java
org.litepal.tablemanager.typechange.BooleanOrm.java
org.litepal.tablemanager.typechange.DateOrm.java
org.litepal.tablemanager.typechange.DecimalOrm.java
org.litepal.tablemanager.typechange.NumericOrm.java
org.litepal.tablemanager.typechange.OrmChange.java
org.litepal.tablemanager.typechange.TextOrm.java
org.litepal.util.BaseUtility.java
org.litepal.util.Const.java
org.litepal.util.DBUtility.java
org.litepal.util.LogUtil.java
org.litepal.util.SharedUtil.java