/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.amber.field;
import com.caucho.amber.expr.AmberExpr;
import com.caucho.amber.expr.PathExpr;
import com.caucho.amber.manager.AmberConnection;
import com.caucho.amber.manager.AmberPersistenceUnit;
import com.caucho.amber.query.QueryParser;
import com.caucho.amber.table.AmberTable;
import com.caucho.amber.table.AmberColumn;
import com.caucho.amber.type.BeanType;
import com.caucho.amber.type.EntityType;
import com.caucho.bytecode.JType;
import com.caucho.bytecode.JTypeWrapper;
import com.caucho.config.ConfigException;
import com.caucho.java.JavaWriter;
import com.caucho.util.CharBuffer;
import com.caucho.util.L10N;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Configuration for a bean's property
*/
abstract public class AbstractField implements AmberField {
private static final L10N L = new L10N(AbstractField.class);
private static final Logger log
= Logger.getLogger(AbstractField.class.getName());
final BeanType _sourceType;
private String _name;
private JType _type;
private Method _getterMethod;
private Method _setterMethod;
private boolean _isLazy = true;
private boolean _isOverride;
private int _updateIndex;
private int _loadGroupIndex = -1;
AbstractField(BeanType sourceType)
{
_sourceType = sourceType;
}
AbstractField(BeanType sourceType, String name)
throws ConfigException
{
this(sourceType);
setName(name);
if (log.isLoggable(Level.FINER))
log.finer(_sourceType + " field " + this);
}
/**
* Sets the name.
*/
public void setName(String name)
throws ConfigException
{
_name = name;
ClassLoader loader
= getSourceType().getPersistenceUnit().getTempClassLoader();
if (! isFieldAccess()) {
char ch = name.charAt(0);
if (Character.isLowerCase(ch))
name = Character.toUpperCase(ch) + name.substring(1);
String getter = "get" + name;
String setter = "set" + name;
_getterMethod = BeanType.getGetter(getBeanClass(), getter);
if (_getterMethod == null) {
getter = "is" + name;
_getterMethod = BeanType.getGetter(getBeanClass(), getter);
}
/* jpa/0u21
if (_getterMethod == null)
throw new ConfigException(L.l("{0}: {1} has no matching getter.",
getBeanClass().getName(), name));
*/
if (_getterMethod == null) {
Field field = BeanType.getField(getBeanClass(), _name);
if (field == null)
throw new ConfigException(L.l("{0}: {1} has no matching field.",
getBeanClass().getName(), _name));
_type = JTypeWrapper.create(field.getGenericType(), loader);
}
else {
_type = JTypeWrapper.create(_getterMethod.getGenericReturnType(),
loader);
_setterMethod = BeanType.getSetter(getBeanClass(), setter);
}
}
else {
Field field = BeanType.getField(getBeanClass(), name);
if (field == null)
throw new ConfigException(L.l("{0}: {1} has no matching field.",
getBeanClass().getName(), name));
_type = JTypeWrapper.create(field.getGenericType(), loader);
}
/*
if (_setterMethod == null && ! isAbstract())
throw new ConfigException(L.l("{0}: {1} has no matching setter.",
getBeanClass().getName(), name));
*/
}
/**
* Returns the field name.
*/
public String getName()
{
return _name;
}
/**
* Sets the java type.
*/
protected void setJavaType(JType type)
{
_type = type;
}
/**
* Returns the owning entity class.
*/
public BeanType getSourceType()
{
return _sourceType;
}
/**
* Returns the amber manager.
*/
public AmberPersistenceUnit getPersistenceUnit()
{
return getSourceType().getPersistenceUnit();
}
/**
* Returns the bean class.
*/
public Class getBeanClass()
{
return getSourceType().getBeanClass();
}
/**
* Returns the source type as
* entity or mapped-superclass.
*/
public EntityType getEntitySourceType()
{
return (EntityType) getSourceType();
}
/**
* Returns the table containing the field's columns.
*/
public AmberTable getTable()
{
return getEntitySourceType().getTable();
}
/**
* Returns the column for the field
*/
public AmberColumn getColumn()
{
return null;
}
/**
* Returns the column for the field
*/
public void setColumn(AmberColumn column)
{
}
/**
* Returns the property index.
*/
public int getIndex()
{
return _updateIndex;
}
/**
* Set the property index.
*/
public void setIndex(int index)
{
_updateIndex = index;
}
/**
* Returns the property's group index.
*/
public int getLoadGroupIndex()
{
return _loadGroupIndex;
}
/**
* Returns the property's group index.
*/
protected void setLoadGroupIndex(int index)
{
_loadGroupIndex = index;
}
/**
* Returns the load group mask.
*/
public long getCreateLoadMask(int group)
{
int index = getLoadGroupIndex();
if (64 * group <= index && index < 64 * (group + 1))
return 1L << (index % 64);
else
return 0;
}
/**
* Returns true for a lazy field.
*/
public boolean isLazy()
{
return _isLazy;
}
/**
* Set true for a lazy field.
*/
public void setLazy(boolean isLazy)
{
_isLazy = isLazy;
}
/**
* Returns true for an override
*/
public boolean isOverride()
{
return _isOverride;
}
/**
* Returns true for an override
*/
public void setOverride(boolean isOverride)
{
_isOverride = isOverride;
}
/**
* Returns true for a key
*/
public boolean isKey()
{
return false;
}
/**
* Returns the getter name.
*/
public String getJavaTypeName()
{
return getJavaTypeName(getJavaClass());
}
/**
* Returns the Java code for the type.
*/
private String getJavaTypeName(Class cl)
{
if (cl.isArray())
return getJavaTypeName(cl.getComponentType()) + "[]";
else
return cl.getName();
}
/**
* Returns the field's type
*/
public JType getJavaType()
{
return _type;
}
/**
* Returns the field's class
*/
public Class getJavaClass()
{
return getJavaType().getRawType().getJavaClass();
}
/**
* Returns true if values are accessed by the fields.
*/
public boolean isFieldAccess()
{
return getSourceType().isFieldAccess();
}
/**
* Returns true if the methods are abstract.
*/
public boolean isAbstract()
{
// jpa/0u21
return (_getterMethod != null
&& Modifier.isAbstract(_getterMethod.getModifiers()));
}
/**
* Returns true if the field is cascadable.
*/
public boolean isCascadable()
{
return false;
}
/**
* Returns true if the methods are abstract.
*/
public boolean isUpdateable()
{
return true;
}
/**
* Creates a copy of the field for a parent
*/
public AmberField override(BeanType table)
{
throw new UnsupportedOperationException(getClass().getName());
}
/**
* Initialize the field.
*/
public void init()
throws ConfigException
{
if (_loadGroupIndex < 0) {
if (_isLazy)
_loadGroupIndex = getEntitySourceType().nextLoadGroupIndex();
else
_loadGroupIndex = getEntitySourceType().getDefaultLoadGroupIndex();
}
}
/**
* Generates the post constructor initialization.
*/
public void generatePostConstructor(JavaWriter out)
throws IOException
{
}
/**
* Generates any prologue.
*/
public void generatePrologue(JavaWriter out, HashSet<Object> completedSet)
throws IOException
{
// CMP
if (isAbstract()) {
out.println();
out.print("public ");
out.print(getJavaTypeName());
out.print(" " + getFieldName() + ";");
}
}
//
// getter/setter code generation
//
/**
* Returns the getter method.
*/
public Method getGetterMethod()
{
return _getterMethod;
}
/**
* Returns the setter method.
*/
public Method getSetterMethod()
{
return _setterMethod;
}
/**
* Returns the getter name.
*/
public String getGetterName()
{
if (isFieldAccess())
return "__caucho_get_" + getName();
else
return _getterMethod.getName();
}
/**
* Returns the setter name.
*/
public String getSetterName()
{
if (isFieldAccess())
return "__caucho_set_" + getName();
else if (_setterMethod != null)
return _setterMethod.getName();
else
return "set" + getGetterName().substring(3);
}
/**
* Returns the actual data.
*/
public String generateSuperGetter(String objThis)
{
if (! getSourceType().isEmbeddable())
return objThis + ".__caucho_super_get_" + getName() + "()";
else if (isFieldAccess())
return objThis + "." + getName();
else
return objThis + "." + getGetterMethod().getName() + "()";
}
/**
* Sets the actual data.
*/
public String generateSuperSetter(String objThis, String value)
{
if (! getSourceType().isEmbeddable())
return objThis + "." + "__caucho_super_set_" + getName() + "(" + value + ")";
else if (isFieldAccess())
return objThis + "." + getName() + " = " + value;
else
return objThis + "." + getSetterMethod().getName() + "(" + value + ")";
}
/**
* Generates the field getter.
*
* @param value the non-null value
*/
public String generateGet(String objThis)
{
if (objThis == null)
return generateNull();
if (objThis.equals("super"))
return generateSuperGetter("this");
else
return objThis + "." + getGetterName() + "()";
/*
else if (! isAbstract())
return obj + "." + _getterMethod.getName() + "()";
else if (_getterMethod != null)
return obj + "." + _getterMethod.getName() + "()";
else
return generateSuperGetter(obj);
*/
}
/**
* Generates the field setter.
*
* @param value the non-null value
*/
public String generateSet(String objThis, String value)
{
if (objThis.equals("super"))
return generateSuperSetter("this", value);
else
return objThis + "." + getSetterName() + "(" + value + ")";
/*
else if (isFieldAccess()) {
// jpa/0h09
return obj + "." + getSetterName() + "(" + value + ")";
}
else if (_setterMethod != null)
return obj + "." + _setterMethod.getName() + "(" + value + ")";
else
return obj + ""; // ejb/0gb9
*/
}
/**
* Generates the field getter.
*
* @param value the non-null value
*/
public void generateGet(JavaWriter out, String objThis)
throws IOException
{
out.print(generateGet(objThis));
}
/**
* Generates set code, which goes through the active calls, i.e.
* not a direct call to the underlying field.
*/
public void generateSet(JavaWriter out, String obj, String value)
throws IOException
{
out.println(generateSet(obj, value) + ";");
}
/**
* Generates the super getter method implementation
*/
public void generateSuperGetterMethod(JavaWriter out)
throws IOException
{
out.println();
out.println("public final " + getJavaTypeName() + " __caucho_super_get_" + getName() + "()");
out.println("{");
out.pushDepth();
if (isAbstract() || getGetterMethod() == null)
out.println("return " + getFieldName() + ";");
else if (this instanceof IdField)
out.println("return " + getGetterName() + "();");
else
out.println("return super." + getGetterName() + "();");
out.popDepth();
out.println("}");
}
/**
* Generates the super setter method implementation
*/
public void generateSuperSetterMethod(JavaWriter out)
throws IOException
{
out.println();
out.println("public final void __caucho_super_set_" + getName() + "(" + getJavaTypeName() + " v)");
out.println("{");
out.pushDepth();
if (isAbstract() || getGetterMethod() == null)
out.println(getFieldName() + " = v;");
else if (getSetterMethod() == null) {
}
else if (this instanceof IdField)
out.println(getSetterMethod().getName() + "(v);");
else
out.println("super." + getSetterMethod().getName() + "(v);");
out.popDepth();
out.println("}");
}
/**
* Generates the getter method implementation.
*/
public void generateGetterMethod(JavaWriter out)
throws IOException
{
}
/**
* Generates the setter method implementation.
*/
public void generateSetterMethod(JavaWriter out)
throws IOException
{
}
/**
* Generates the detachment code
*/
public void generateDetach(JavaWriter out)
throws IOException
{
}
//
// SQL generation
//
/**
* Generates the select clause for an entity load.
*/
public String generateLoadSelect(AmberTable table, String id)
{
return null;
}
/**
* Generates the select clause.
*/
public String generateSelect(String id)
{
return null;
}
/**
* Generates the JPA QL select clause.
*/
public String generateJavaSelect(String id)
{
return null;
}
/**
* Generates the where clause.
*/
public String generateWhere(String id)
{
return null;
}
/**
* Generates the where clause.
*/
public void generateUpdate(CharBuffer sql)
{
}
/**
* Generates loading cache
*/
public void generateUpdate(JavaWriter out, String maskVar, String pstmt,
String index)
throws IOException
{
int group = getIndex() / 64;
long mask = 1L << getIndex() % 64;
out.println();
out.println("if ((" + maskVar + "_" + group + " & " + mask + "L) != 0) {");
out.pushDepth();
generateStatementSet(out, pstmt, index);
out.popDepth();
out.println("}");
}
/**
* Generates loading code
*/
public boolean hasLoadGroup(int index)
{
return index == _loadGroupIndex;
}
/**
* Generates loading code
*/
public int generateLoad(JavaWriter out, String rs,
String indexVar, int index)
throws IOException
{
return index;
}
/**
* Generates loading code after the basic fields.
*/
public int generatePostLoadSelect(JavaWriter out, int index)
throws IOException
{
return index;
}
/**
* Generates loading cache
*/
public void generateLoadFromObject(JavaWriter out, String obj)
throws IOException
{
if (getGetterMethod() == null || getSetterMethod() == null)
return;
String getter = getGetterName();
String loadVar = "__caucho_loadMask_" + (getLoadGroupIndex() / 64);
long loadMask = (1L << getLoadGroupIndex());
out.println("if ((" + loadVar + " & " + loadMask + "L) != 0)");
out.print(" ");
out.println(" " + generateSuperSetter("this", generateGet(obj)) + ";");
}
/**
* Generates loading for a native query
*/
public int generateLoadNative(JavaWriter out, int index)
throws IOException
{
return index;
}
/**
* Generates loading for a native query
*/
public void generateNativeColumnNames(ArrayList<String> names)
{
}
/**
* Generates loading cache
*/
public void generateUpdateFromObject(JavaWriter out, String obj)
throws IOException
{
out.println(generateSuperSetter("this", generateGet(obj)) + ";");
}
/**
* Returns the null value.
*/
public String generateNull()
{
return "null";
}
/**
* Returns the field name.
*/
protected String getFieldName()
{
return getName();
}
/**
* Generates the insert.
*/
public final String generateInsert()
{
return null;
}
/**
* Generates the insert.
*/
public void generateInsertColumns(ArrayList<String> columns)
{
}
/**
* Generates the table create.
*/
public String generateCreateTableSQL(AmberPersistenceUnit manager)
{
return null;
}
/**
* Generates the set clause.
*/
public void generateStatementSet(JavaWriter out, String pstmt, String index)
throws IOException
{
generateStatementSet(out, pstmt, index, "super");
}
/**
* Generates the set clause for the insert clause.
*/
public void generateInsertSet(JavaWriter out, String pstmt,
String index, String obj)
throws IOException
{
generateStatementSet(out, pstmt, index, obj);
}
/**
* Generates the set clause for the insert clause.
*/
public void generateUpdateSet(JavaWriter out, String pstmt,
String index, String obj)
throws IOException
{
generateStatementSet(out, pstmt, index, obj);
}
/**
* Generates any code needed before a persist occurs
*/
public void generatePrePersist(JavaWriter out)
throws IOException
{
}
/**
* Updates the cached copy.
*/
public void generateCopyUpdateObject(JavaWriter out,
String dst, String src,
int updateIndex)
throws IOException
{
// commented out: jpa/0l03
if (getIndex() == updateIndex) {
String value = generateGet(src);
out.println(generateSet(dst, value) + ";");
}
}
/**
* Updates the cached copy.
*/
public void generateCopyLoadObject(JavaWriter out,
String dst, String src,
int loadIndex)
throws IOException
{
// jpa/0g0l
if (getLoadGroupIndex() != loadIndex)
return;
String value = generateGet(src);
// jpa/0l43 out.println(generateStatementSet(dst, value) + ";");
boolean isJPA = getEntitySourceType().getPersistenceUnit().isJPA();
if (isJPA
&& ! (dst.equals("cacheEntity")
|| dst.equals("super")
|| dst.equals("item"))) {
// jpa/0j5fn: merge()
out.println("if (isFullMerge)");
out.println(" " + generateSet(dst, value) + ";");
out.println("else");
out.print(" ");
}
if (! dst.equals("super"))
out.println(generateSuperSetter(dst, value) + ";");
else
out.println(generateSuperSetter("this", value) + ";");
}
/**
* Updates the cached copy.
*/
public void generateMergeFrom(JavaWriter out,
String dst, String src)
throws IOException
{
// jpa/0g0l
//if (getLoadGroupIndex() != loadIndex)
// return;
String value = generateGet(src);
// jpa/0l43
out.println(generateSet(dst, value) + ";");
}
/**
* Checks entity-relationships from an object.
*/
public void generateDumpRelationships(JavaWriter out,
int updateIndex)
throws IOException
{
}
/**
* Generates the set clause.
*/
public void generateStatementSet(JavaWriter out, String pstmt,
String index, String obj)
throws IOException
{
}
/**
* Converts to an object.
*/
public String toObject(String value)
{
return value;
}
/**
* Links to the target.
*/
public void link()
{
}
/**
* Generates the pre-delete code
*/
public void generatePreDelete(JavaWriter out)
throws IOException
{
}
/**
* Generates the delete foreign
*/
public void generatePostDelete(JavaWriter out)
throws IOException
{
}
/**
* Generates the expire code
*/
public void generateExpire(JavaWriter out)
throws IOException
{
}
/**
* Generates code for foreign entity create/delete
*/
public void generateInvalidateForeign(JavaWriter out)
throws IOException
{
}
/**
* Deletes the children
*/
public void childDelete(AmberConnection aConn, Serializable primaryKey)
throws SQLException
{
}
/**
* Generates code to convert to the type from the object.
*/
public String generateCastFromObject(String value)
{
return value;
}
/**
* Generates code to test the equals.
*/
public String generateEquals(String leftBase, String value)
{
return leftBase + ".equals(" + value + ")";
}
/**
* Creates the expression for the field.
*/
public AmberExpr createExpr(QueryParser parser, PathExpr parent)
{
throw new UnsupportedOperationException(getClass().getName());
}
@Override
public String toString()
{
return getClass().getSimpleName() + "[" + getName() + "," + getSourceType() + "]";
}
}