0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: * Free Software Foundation, Inc.
0023: * 59 Temple Place, Suite 330
0024: * Boston, MA 02111-1307 USA
0025: *
0026: * @author Scott Ferguson
0027: */
0028:
0029: package com.caucho.amber.field;
0030:
0031: import com.caucho.amber.AmberRuntimeException;
0032: import com.caucho.amber.cfg.*;
0033: import com.caucho.amber.expr.AmberExpr;
0034: import com.caucho.amber.expr.ManyToOneExpr;
0035: import com.caucho.amber.expr.PathExpr;
0036: import com.caucho.amber.query.QueryParser;
0037: import com.caucho.amber.table.Column;
0038: import com.caucho.amber.table.ForeignColumn;
0039: import com.caucho.amber.table.LinkColumns;
0040: import com.caucho.amber.table.Table;
0041: import com.caucho.amber.type.*;
0042: import com.caucho.bytecode.JAnnotation;
0043: import com.caucho.config.ConfigException;
0044: import com.caucho.java.JavaWriter;
0045: import com.caucho.log.Log;
0046: import com.caucho.util.CharBuffer;
0047: import com.caucho.util.L10N;
0048:
0049: import javax.persistence.CascadeType;
0050: import java.io.IOException;
0051: import java.util.ArrayList;
0052: import java.util.HashMap;
0053: import java.util.HashSet;
0054: import java.util.logging.Logger;
0055:
0056: /**
0057: * Represents a many-to-one link pointing to an entity.
0058: */
0059: public class EntityManyToOneField extends CascadableField {
0060: private static final L10N L = new L10N(EntityManyToOneField.class);
0061: private static final Logger log = Log
0062: .open(EntityManyToOneField.class);
0063:
0064: private LinkColumns _linkColumns;
0065:
0066: private RelatedType _targetType;
0067:
0068: private int _targetLoadIndex;
0069:
0070: private DependentEntityOneToOneField _targetField;
0071: private AmberField _aliasField;
0072:
0073: private boolean _isInsert = true;
0074: private boolean _isUpdate = true;
0075:
0076: private boolean _isSourceCascadeDelete;
0077: private boolean _isTargetCascadeDelete;
0078:
0079: private boolean _isManyToOne;
0080:
0081: private Object _joinColumnsAnn[];
0082: private HashMap<String, JoinColumnConfig> _joinColumnMap = null;
0083:
0084: public EntityManyToOneField(RelatedType relatedType, String name,
0085: CascadeType[] cascadeType, boolean isManyToOne)
0086: throws ConfigException {
0087: super (relatedType, name, cascadeType);
0088:
0089: _isManyToOne = isManyToOne;
0090: }
0091:
0092: public EntityManyToOneField(RelatedType relatedType, String name,
0093: CascadeType[] cascadeType) throws ConfigException {
0094: super (relatedType, name, cascadeType);
0095: }
0096:
0097: public EntityManyToOneField(RelatedType relatedType, String name)
0098: throws ConfigException {
0099: this (relatedType, name, null);
0100: }
0101:
0102: public EntityManyToOneField(RelatedType relatedType) {
0103: super (relatedType);
0104: }
0105:
0106: /**
0107: * Sets the target type.
0108: */
0109: public void setType(Type targetType) {
0110: if (!(targetType instanceof RelatedType))
0111: throw new AmberRuntimeException(L.l(
0112: "many-to-one requires an entity target at '{0}'",
0113: targetType));
0114:
0115: _targetType = (RelatedType) targetType;
0116: }
0117:
0118: /**
0119: * Returns the source type as
0120: * entity or mapped-superclass.
0121: */
0122: public RelatedType getRelatedType() {
0123: return (RelatedType) getSourceType();
0124: }
0125:
0126: /**
0127: * Returns the target type as
0128: * entity or mapped-superclass.
0129: */
0130: public RelatedType getEntityTargetType() {
0131: return _targetType;
0132: }
0133:
0134: /**
0135: * Returns the foreign type.
0136: */
0137: public String getForeignTypeName() {
0138: //return ((KeyColumn) getColumn()).getType().getForeignTypeName();
0139: return getEntityTargetType().getForeignTypeName();
0140: }
0141:
0142: /**
0143: * Returns true if it is annotated as many-to-one.
0144: */
0145: public boolean isAnnotatedManyToOne() {
0146: return _isManyToOne;
0147: }
0148:
0149: /**
0150: * Set true if deletes cascade to the target.
0151: */
0152: public void setTargetCascadeDelete(boolean isCascadeDelete) {
0153: _isTargetCascadeDelete = isCascadeDelete;
0154: }
0155:
0156: /**
0157: * Set true if deletes cascade to the source.
0158: */
0159: public void setSourceCascadeDelete(boolean isCascadeDelete) {
0160: _isSourceCascadeDelete = isCascadeDelete;
0161: }
0162:
0163: /**
0164: * Set true if deletes cascade to the target.
0165: */
0166: public boolean isTargetCascadeDelete() {
0167: return _isTargetCascadeDelete;
0168: }
0169:
0170: /**
0171: * Set true if deletes cascade to the source.
0172: */
0173: public boolean isSourceCascadeDelete() {
0174: return _isSourceCascadeDelete;
0175: }
0176:
0177: /**
0178: * Sets the join column annotations.
0179: */
0180: public void setJoinColumns(Object joinColumnsAnn[]) {
0181: _joinColumnsAnn = joinColumnsAnn;
0182: }
0183:
0184: /**
0185: * Gets the join column annotations.
0186: */
0187: public Object[] getJoinColumns() {
0188: return _joinColumnsAnn;
0189: }
0190:
0191: /**
0192: * Sets the join column map.
0193: */
0194: public void setJoinColumnMap(
0195: HashMap<String, JoinColumnConfig> joinColumnMap) {
0196: _joinColumnMap = joinColumnMap;
0197: }
0198:
0199: /**
0200: * Gets the join column map.
0201: */
0202: public HashMap<String, JoinColumnConfig> getJoinColumnMap() {
0203: return _joinColumnMap;
0204: }
0205:
0206: /**
0207: * Sets the join columns.
0208: */
0209: public void setLinkColumns(LinkColumns linkColumns) {
0210: _linkColumns = linkColumns;
0211: }
0212:
0213: /**
0214: * Gets the columns.
0215: */
0216: public LinkColumns getLinkColumns() {
0217: return _linkColumns;
0218: }
0219:
0220: /**
0221: * Sets the target field.
0222: */
0223: public void setTargetField(DependentEntityOneToOneField field) {
0224: _targetField = field;
0225: }
0226:
0227: /**
0228: * Sets any alias field.
0229: */
0230: public void setAliasField(AmberField alias) {
0231: _aliasField = alias;
0232: }
0233:
0234: /**
0235: * Overrides the load-group index to
0236:
0237: /**
0238: * Initializes the field.
0239: */
0240: public void init() throws ConfigException {
0241: init(getRelatedType());
0242: }
0243:
0244: /**
0245: * Initializes the field.
0246: */
0247: public void init(RelatedType relatedType) throws ConfigException {
0248: boolean isJPA = relatedType.getPersistenceUnit().isJPA();
0249:
0250: int loadGroupIndex = getEntitySourceType()
0251: .getDefaultLoadGroupIndex();
0252: super .setLoadGroupIndex(loadGroupIndex);
0253:
0254: // jpa/0l40 vs. ejb/0602
0255: if (isJPA)
0256: _targetLoadIndex = loadGroupIndex;
0257: else
0258: _targetLoadIndex = relatedType.nextLoadGroupIndex();
0259:
0260: Table sourceTable = relatedType.getTable();
0261:
0262: if (sourceTable == null || !isJPA) {
0263: // jpa/0ge3, ejb/0602
0264: super .init();
0265: return;
0266: }
0267:
0268: // jpa/0j67
0269: setSourceCascadeDelete(isCascade(CascadeType.REMOVE));
0270:
0271: int n = 0;
0272:
0273: if (_joinColumnMap != null)
0274: n = _joinColumnMap.size();
0275:
0276: ArrayList<ForeignColumn> foreignColumns = new ArrayList<ForeignColumn>();
0277:
0278: RelatedType parentType = _targetType;
0279:
0280: ArrayList<Column> targetIdColumns = _targetType.getId()
0281: .getColumns();
0282:
0283: while (targetIdColumns.size() == 0) {
0284:
0285: parentType = parentType.getParentType();
0286:
0287: if (parentType == null)
0288: break;
0289:
0290: targetIdColumns = parentType.getId().getColumns();
0291: }
0292:
0293: for (Column keyColumn : targetIdColumns) {
0294:
0295: String columnName;
0296:
0297: if (isJPA)
0298: columnName = getName().toUpperCase() + '_'
0299: + keyColumn.getName();
0300: else {
0301: // ejb/0602
0302: columnName = keyColumn.getName();
0303: }
0304:
0305: boolean nullable = true;
0306: boolean unique = false;
0307:
0308: if (n > 0) {
0309: JoinColumnConfig joinColumn;
0310:
0311: if (n == 1) {
0312: joinColumn = (JoinColumnConfig) _joinColumnMap
0313: .values().toArray()[0];
0314: } else
0315: joinColumn = _joinColumnMap
0316: .get(keyColumn.getName());
0317:
0318: if (joinColumn != null) {
0319: columnName = joinColumn.getName();
0320:
0321: nullable = joinColumn.getNullable();
0322: unique = joinColumn.getUnique();
0323: }
0324: } else {
0325: JAnnotation joinAnn = BaseConfigIntrospector
0326: .getJoinColumn(_joinColumnsAnn, keyColumn
0327: .getName());
0328:
0329: if (joinAnn != null) {
0330: columnName = joinAnn.getString("name");
0331:
0332: nullable = joinAnn.getBoolean("nullable");
0333: unique = joinAnn.getBoolean("unique");
0334: }
0335: }
0336:
0337: ForeignColumn foreignColumn;
0338:
0339: foreignColumn = sourceTable.createForeignColumn(columnName,
0340: keyColumn);
0341:
0342: foreignColumn.setNotNull(!nullable);
0343: foreignColumn.setUnique(unique);
0344:
0345: foreignColumns.add(foreignColumn);
0346: }
0347:
0348: LinkColumns linkColumns = new LinkColumns(sourceTable,
0349: _targetType.getTable(), foreignColumns);
0350:
0351: setLinkColumns(linkColumns);
0352:
0353: super .init();
0354:
0355: Id id = getEntityTargetType().getId();
0356: ArrayList<Column> keys = id.getColumns();
0357:
0358: if (_linkColumns == null) {
0359: ArrayList<ForeignColumn> columns = new ArrayList<ForeignColumn>();
0360:
0361: for (int i = 0; i < keys.size(); i++) {
0362: Column key = keys.get(i);
0363:
0364: String name;
0365:
0366: if (keys.size() == 1)
0367: name = getName();
0368: else
0369: name = getName() + "_" + key.getName();
0370:
0371: columns.add(sourceTable.createForeignColumn(name, key));
0372: }
0373:
0374: _linkColumns = new LinkColumns(relatedType.getTable(),
0375: _targetType.getTable(), columns);
0376: }
0377:
0378: if (relatedType.getId() != null) {
0379: // resolve any alias
0380: for (AmberField field : relatedType.getId().getKeys()) {
0381: for (ForeignColumn column : _linkColumns.getColumns()) {
0382: if (field.getColumn() != null
0383: && field.getColumn().getName().equals(
0384: column.getName())) {
0385: _aliasField = field;
0386: }
0387: }
0388: }
0389: }
0390:
0391: _targetLoadIndex = relatedType.getLoadGroupIndex(); // nextLoadGroupIndex();
0392:
0393: _linkColumns.setTargetCascadeDelete(isTargetCascadeDelete());
0394: _linkColumns.setSourceCascadeDelete(isSourceCascadeDelete());
0395: }
0396:
0397: /**
0398: * Generates the post constructor initialization.
0399: */
0400: public void generatePostConstructor(JavaWriter out)
0401: throws IOException {
0402: if (_aliasField == null) {
0403: out.println(getSetterName() + "(" + generateSuperGetter()
0404: + ");");
0405: }
0406: }
0407:
0408: /**
0409: * Creates the expression for the field.
0410: */
0411: public AmberExpr createExpr(QueryParser parser, PathExpr parent) {
0412: return new ManyToOneExpr(parent, _linkColumns);
0413: }
0414:
0415: /**
0416: * Gets the column corresponding to the target field.
0417: */
0418: public ForeignColumn getColumn(Column targetColumn) {
0419: return _linkColumns.getSourceColumn(targetColumn);
0420: }
0421:
0422: /**
0423: * Generates the insert.
0424: */
0425: public void generateInsertColumns(ArrayList<String> columns) {
0426: if (_isInsert && _aliasField == null)
0427: _linkColumns.generateInsert(columns);
0428: }
0429:
0430: /**
0431: * Generates the select clause.
0432: */
0433: public String generateLoadSelect(Table table, String id) {
0434: if (_aliasField != null)
0435: return null;
0436:
0437: if (_linkColumns == null) {
0438: // jpa/0ge3
0439: return null;
0440: }
0441:
0442: if (_linkColumns.getSourceTable() != table)
0443: return null;
0444: else
0445: return _linkColumns.generateSelectSQL(id);
0446: }
0447:
0448: /**
0449: * Generates the select clause.
0450: */
0451: public String generateSelect(String id) {
0452: if (_aliasField != null)
0453: return null;
0454:
0455: return _linkColumns.generateSelectSQL(id);
0456: }
0457:
0458: /**
0459: * Generates the update set clause
0460: */
0461: public void generateUpdate(CharBuffer sql) {
0462: if (_aliasField != null)
0463: return;
0464:
0465: if (_isUpdate) {
0466: sql.append(_linkColumns.generateUpdateSQL());
0467: }
0468: }
0469:
0470: /**
0471: * Generates any prologue.
0472: */
0473: public void generatePrologue(JavaWriter out,
0474: HashSet<Object> completedSet) throws IOException {
0475: super .generatePrologue(out, completedSet);
0476:
0477: out.println();
0478:
0479: Id id = getEntityTargetType().getId();
0480:
0481: out.println("protected transient " + id.getForeignTypeName()
0482: + " __caucho_field_" + getName() + ";");
0483:
0484: if (_aliasField == null) {
0485: id.generatePrologue(out, completedSet, getName());
0486: }
0487: }
0488:
0489: /**
0490: * Generates the linking for a join
0491: */
0492: public void generateJoin(CharBuffer cb, String sourceTable,
0493: String targetTable) {
0494: cb.append(_linkColumns.generateJoin(sourceTable, targetTable));
0495: }
0496:
0497: /**
0498: * Generates loading code
0499: */
0500: public int generateLoad(JavaWriter out, String rs, String indexVar,
0501: int index) throws IOException {
0502: if (_aliasField != null)
0503: return index;
0504:
0505: out.print("__caucho_field_" + getName() + " = ");
0506:
0507: index = getEntityTargetType().getId().generateLoadForeign(out,
0508: rs, indexVar, index, getName());
0509:
0510: out.println(";");
0511:
0512: /*
0513: // ejb/0a06
0514: String proxy = "aConn.loadProxy(\"" + getEntityTargetType().getName() + "\", __caucho_field_" + getName() + ")";
0515:
0516: proxy = "(" + getEntityTargetType().getProxyClass().getName() + ") " + proxy;
0517:
0518: out.println(generateSuperSetter(proxy) + ";");
0519: */
0520:
0521: // commented out jpa/0l40
0522: // out.println(generateSuperSetter("null") + ";");
0523: int group = _targetLoadIndex / 64;
0524: long mask = (1L << (_targetLoadIndex % 64));
0525:
0526: //out.println("__caucho_loadMask_" + group + " &= ~" + mask + "L;");
0527:
0528: return index;
0529: }
0530:
0531: /* XXX: moved to generatePostLoadSelect()
0532: * Generates loading code
0533: *
0534: public int generateLoadEager(JavaWriter out, String rs,
0535: String indexVar, int index)
0536: throws IOException
0537: {
0538: }
0539: */
0540:
0541: /**
0542: * Generates loading code after the basic fields.
0543: */
0544: @Override
0545: public int generatePostLoadSelect(JavaWriter out, int index)
0546: throws IOException {
0547: if (!isLazy()) {
0548: int group = _targetLoadIndex / 64;
0549: long mask = (1L << (_targetLoadIndex % 64));
0550: String loadVar = "__caucho_loadMask_" + group;
0551:
0552: // commented out jpa/0l40
0553: // out.println(loadVar + " |= " + mask + "L;");
0554:
0555: String javaType = getJavaTypeName();
0556:
0557: // jpa/0o05
0558: String indexS = "_" + group + "_" + index;
0559:
0560: // generateLoadProperty(out, indexS, "__caucho_session");
0561:
0562: out.println(getGetterName() + "();");
0563: }
0564:
0565: return ++index;
0566: }
0567:
0568: /**
0569: * Generates the get property.
0570: */
0571: public void generateGetProperty(JavaWriter out) throws IOException {
0572: // jpa/0h07, jpa/0h08
0573: // jpa/0o03, jpa/0o05, jpa/0o09
0574: // jpa/0s2d, jpa/1810
0575:
0576: String javaType = getJavaTypeName();
0577:
0578: out.println();
0579: out
0580: .println("public " + javaType + " " + getGetterName()
0581: + "()");
0582: out.println("{");
0583: out.pushDepth();
0584:
0585: int keyLoadIndex = getLoadGroupIndex();
0586: int entityLoadIndex = _targetLoadIndex;
0587:
0588: int group = entityLoadIndex / 64;
0589: long mask = (1L << (entityLoadIndex % 64));
0590: String loadVar = "__caucho_loadMask_" + group;
0591:
0592: /*
0593: out.println();
0594: out.println("public " + javaType + " __caucho_item_" + getGetterName() + "(com.caucho.amber.manager.AmberConnection aConn)");
0595: out.println("{");
0596: out.pushDepth();
0597:
0598: out.println("if (aConn != null) {");
0599: */
0600:
0601: // jpa/0h29
0602: out
0603: .println("if (__caucho_session != null && __caucho_state != com.caucho.amber.entity.EntityState.P_DELETED) {");
0604:
0605: /* XXX: jpa/0h04
0606: if (isLazy())
0607: out.println(" && (" + loadVar + " & " + mask + "L) == 0) {");
0608: else {
0609: // jpa/0o03
0610: out.println(") {");
0611: }
0612: */
0613:
0614: out.pushDepth();
0615:
0616: // jpa/0o05, jpa/0ge3
0617: // out.println("com.caucho.amber.entity.Entity contextEntity = __caucho_session.getEntity((com.caucho.amber.entity.Entity) this);");
0618:
0619: // jpa/0ge3
0620: out
0621: .println("com.caucho.amber.entity.Entity contextEntity = (com.caucho.amber.entity.Entity) this;");
0622:
0623: out.println();
0624:
0625: String index = "_" + group;
0626: index += "_" + mask;
0627:
0628: if (_aliasField == null) {
0629: // XXX: possibly bypassing of caching
0630: out.println("if ((" + loadVar + " & " + mask + "L) == 0)");
0631: out.println(" __caucho_load_select_" + getLoadGroupIndex()
0632: + "(__caucho_session);");
0633: }
0634:
0635: out.println(loadVar + " |= " + mask + "L;");
0636:
0637: String varName = generateLoadProperty(out, index,
0638: "__caucho_session");
0639:
0640: out.println("return " + varName + ";");
0641:
0642: out.popDepth();
0643: out.println("}");
0644:
0645: out.println();
0646: out.println("return " + generateSuperGetter() + ";");
0647:
0648: out.popDepth();
0649: out.println("}");
0650: }
0651:
0652: /**
0653: * Generates the set property.
0654: */
0655: public String generateLoadProperty(JavaWriter out, String index,
0656: String session) throws IOException {
0657: String javaType = getJavaTypeName();
0658:
0659: boolean isJPA = getRelatedType().getPersistenceUnit().isJPA();
0660:
0661: String targetTypeExt = _targetType.getInstanceClassName();
0662:
0663: String otherKey;
0664:
0665: if (_aliasField == null)
0666: otherKey = "__caucho_field_" + getName();
0667: else
0668: otherKey = _aliasField.generateGet("super");
0669:
0670: String proxyType = getEntityTargetType().getProxyClass()
0671: .getName();
0672: boolean isProxy = !isJPA;
0673:
0674: String varName = "v" + index;
0675: String proxyVarName;
0676:
0677: if (isProxy)
0678: proxyVarName = "p" + index;
0679: else
0680: proxyVarName = varName;
0681:
0682: if (isProxy)
0683: out.println(proxyType + " " + proxyVarName + " = null;");
0684:
0685: out.println(targetTypeExt + " " + varName + " = null;");
0686:
0687: out.println();
0688:
0689: Id id = getEntityTargetType().getId();
0690:
0691: // jpa/0s2d
0692: String nullTest = otherKey + " != null";
0693:
0694: /* XXX
0695: if (id instanceof CompositeId) {
0696: }
0697: else {
0698: KeyPropertyField key = (KeyPropertyField) id.getKeys().get(0);
0699: nullTest = key.getColumn().getType().generateIsNotNull(otherKey);
0700: }
0701: */
0702:
0703: // jpa/0h27
0704: out.println("if (" + nullTest + ") {");
0705: out.pushDepth();
0706:
0707: /*
0708: out.println("try {");
0709: out.pushDepth();
0710:
0711: // jpa/0o03, jpa/0o09, jpa/0l42: adds the other end to the context and
0712: // sets its load mask bit corresponding to this side. This way,
0713: // the other end will not need to reload the current entity.
0714: out.println(varName + " = (" + targetTypeExt + ") " + session + ".addNewEntity(" + targetTypeExt + ".class, " + otherKey + ");");
0715:
0716: // jpa/0l43
0717: out.println();
0718: out.println("if (" + varName + " == null)");
0719: out.println(" " + varName + " = (" + targetTypeExt + ") " + session + ".getSubEntity(" + targetTypeExt + ".class, " + otherKey + ");");
0720:
0721: out.popDepth();
0722: out.println("} catch (RuntimeException e) {");
0723: out.println(" throw e;");
0724: out.println("} catch (Exception e) {");
0725: out.println(" throw new com.caucho.amber.AmberRuntimeException(e);");
0726: out.println("}");
0727: */
0728:
0729: long targetGroup = 0;
0730: long targetMask = 0;
0731:
0732: // jpa/0s2e as a negative test.
0733: if (_targetField != null) {
0734: // jpa/0l42
0735: long targetLoadIndex = _targetField.getTargetLoadIndex();
0736: targetGroup = targetLoadIndex / 64;
0737: targetMask = (1L << (targetLoadIndex % 64));
0738: }
0739:
0740: out.println(varName + " = (" + targetTypeExt + ") " + session
0741: + ".loadEntity(" + targetTypeExt + ".class, "
0742: + otherKey + ", false);");
0743:
0744: // jpa/0j67
0745: out.println("if (" + varName + " != null && " + varName
0746: + " != " + generateSuperGetter() + ") {");
0747: out.pushDepth();
0748:
0749: // ejb/069a
0750: if (isJPA)
0751: out.println(generateSuperSetter(varName) + ";");
0752:
0753: generateSetTargetLoadMask(out, varName);
0754:
0755: out.println(varName + ".__caucho_retrieve_eager(" + session
0756: + ");");
0757:
0758: out.popDepth();
0759: out.println("}");
0760:
0761: // ejb/06h0, jpa/0o03
0762: if (isAbstract() && (isLazy() || !isJPA)) {
0763: String proxy = session + ".loadProxy(\""
0764: + getEntityTargetType().getName()
0765: + "\", __caucho_field_" + getName() + ")";
0766:
0767: // jpa/0o09
0768: if (isJPA)
0769: proxyType = targetTypeExt;
0770:
0771: proxy = proxyVarName + " = (" + proxyType + ") " + proxy
0772: + ";";
0773:
0774: out.println(proxy);
0775: } else {
0776: /*
0777: // jpa/0h24
0778: out.println("if (! " + varName + ".__caucho_getEntityState().isManaged()) {");
0779: out.pushDepth();
0780:
0781: long targetMask = 0;
0782: long targetGroup = 0;
0783:
0784: if (_targetField != null) {
0785: long targetLoadIndex = _targetField.getTargetLoadIndex();
0786: targetGroup = targetLoadIndex / 64;
0787: targetMask = (1L << (targetLoadIndex % 64));
0788: }
0789:
0790: // jpa/0o03, jpa/0ge4
0791: out.println(session + ".loadFromHome(" + targetTypeExt + ".class.getName(), " + otherKey + ", " + targetMask + ", " + targetGroup + ");");
0792: out.popDepth();
0793: out.println("}");
0794: */
0795: }
0796:
0797: out.popDepth();
0798: out.println("}");
0799:
0800: out.println(generateSuperSetter(proxyVarName) + ";");
0801:
0802: return proxyVarName;
0803: }
0804:
0805: void generateSetTargetLoadMask(JavaWriter out, String varName)
0806: throws IOException {
0807: // jpa/0o0-, jpa/0ge4
0808: boolean isJPA = getRelatedType().getPersistenceUnit().isJPA();
0809:
0810: if (_targetField != null && isJPA) {
0811: long targetLoadIndex = _targetField.getTargetLoadIndex();
0812: long targetGroup = targetLoadIndex / 64;
0813: long targetMask = (1L << (targetLoadIndex % 64));
0814:
0815: out.println(varName + ".__caucho_setLoadMask(" + varName
0816: + ".__caucho_getLoadMask(" + targetGroup + ") | "
0817: + targetMask + "L, " + targetGroup + ");");
0818:
0819: varName = "((" + _targetType.getInstanceClassName() + ") "
0820: + varName + ")";
0821:
0822: // jpa/0ge4
0823: String this ContextEntity = "("
0824: + _targetField.getJavaTypeName() /* getSourceType().getInstanceClassName() */
0825: + ") contextEntity";
0826:
0827: // jpa/0o05
0828: out.println(_targetField.generateSuperSetter(varName,
0829: this ContextEntity)
0830: + ";");
0831: }
0832: }
0833:
0834: /**
0835: * Updates the cached copy.
0836: */
0837: public void generateCopyUpdateObject(JavaWriter out, String dst,
0838: String src, int updateIndex) throws IOException {
0839: // jpa/0s29, jpa/0s2j
0840: if (getIndex() != updateIndex)
0841: return;
0842:
0843: // order matters: ejb/06gc
0844: String var = "__caucho_field_" + getName();
0845: out.println(generateAccessor(dst, var) + " = "
0846: + generateAccessor(src, var) + ";");
0847:
0848: /* The cache update is handled copying only the key.
0849: // ejb/0627
0850: if (getRelatedType().getPersistenceUnit().isJPA()) {
0851: // jpa/0h0a
0852:
0853: String value = generateGet(src);
0854:
0855: value = "(" + _targetType.getInstanceClassName() + ") aConn.getEntity((com.caucho.amber.entity.Entity) " + value + ")";
0856:
0857: out.println(generateSet(dst, value) + ";");
0858: }
0859: */
0860: }
0861:
0862: /**
0863: * Updates the cached copy.
0864: */
0865: public void generateCopyLoadObject(JavaWriter out, String dst,
0866: String src, int updateIndex) throws IOException {
0867: if (getLoadGroupIndex() != updateIndex)
0868: return;
0869:
0870: String var = "__caucho_field_" + getName();
0871:
0872: boolean isJPA = getEntitySourceType().getPersistenceUnit()
0873: .isJPA();
0874:
0875: // order matters: jpa/0h08, jpa/0h09
0876:
0877: // jpa/0h20, jpa/0o08, ejb/06--, ejb/0a-- and jpa/0o04
0878: // ejb/0628 vs. jpa/0h0a
0879: if (isJPA
0880: && !(dst.equals("cacheEntity") || dst.equals("super") || dst
0881: .equals("item"))) {
0882: String value = generateGet(src);
0883:
0884: out.println("// " + dst);
0885:
0886: if (_targetType instanceof EntityType) {
0887: String targetTypeExt = getEntityTargetType()
0888: .getInstanceClassName();
0889:
0890: // jpa/0s2e
0891: out.println("if (isFullMerge)");
0892: out.println(" child = " + value + ";");
0893: out.println("else {");
0894: out.pushDepth();
0895:
0896: // jpa/0h0a: gets the cache object to copy from.
0897: out.println("child = aConn.getCacheEntity("
0898: + targetTypeExt + ".class, " + var + ");");
0899:
0900: // jpa/0o36: the cache item is only available after commit.
0901: out.println();
0902: out.println("if (child == null && " + value
0903: + " != null)");
0904: out
0905: .println(" child = ((com.caucho.amber.entity.Entity) "
0906: + value
0907: + ").__caucho_getCacheEntity();");
0908:
0909: out.popDepth();
0910: out.println("}");
0911: } else {
0912: // XXX: jpa/0l14
0913: out.println("child = null;");
0914: }
0915:
0916: out.println("if (child != null) {");
0917: out.pushDepth();
0918:
0919: String targetTypeExt = getEntityTargetType()
0920: .getInstanceClassName();
0921:
0922: out.println("if (isFullMerge) {");
0923: out.pushDepth();
0924:
0925: // jpa/0j5f
0926: out
0927: .println("child = aConn.load(child.getClass(), ((com.caucho.amber.entity.Entity) child).__caucho_getPrimaryKey(), true);");
0928:
0929: out.popDepth();
0930: out.println("} else {");
0931: out.pushDepth();
0932:
0933: // jpa/0l42
0934: out
0935: .println("com.caucho.amber.entity.Entity newChild = aConn.addNewEntity(child.getClass(), ((com.caucho.amber.entity.Entity) child).__caucho_getPrimaryKey());");
0936:
0937: out.println("if (newChild == null) {");
0938: out.pushDepth();
0939:
0940: value = "aConn.getEntity((com.caucho.amber.entity.Entity) child)";
0941:
0942: out.println("newChild = " + value + ";");
0943:
0944: out.popDepth();
0945: out.println("} else {");
0946: out.pushDepth();
0947:
0948: // jpa/0h13
0949: out
0950: .println("((com.caucho.amber.entity.Entity) child).__caucho_copyTo(newChild, aConn, (com.caucho.amber.entity.EntityItem) null);");
0951:
0952: out.popDepth();
0953: out.println("}");
0954:
0955: out.println("child = newChild;");
0956:
0957: out.popDepth();
0958: out.println("}");
0959:
0960: out.popDepth();
0961: out.println("}");
0962:
0963: value = "(" + targetTypeExt + ") child";
0964:
0965: // XXX: jpa/0l43
0966: out.println("if (isFullMerge)");
0967: out.println(" " + generateSet(dst, value) + ";");
0968: }
0969:
0970: // jpa/0o05
0971: // if (getLoadGroupIndex() == updateIndex) {
0972:
0973: // order matters: ejb/06gc
0974: out.println(generateAccessor(dst, var) + " = "
0975: + generateAccessor(src, var) + ";");
0976:
0977: // jpa/0o08, jpa/0o04
0978: if (!dst.equals("cacheEntity")) {
0979: // jpa/0o05, jpa/0h20
0980: if (!(dst.equals("super") || dst.equals("item"))) { // || isLazy())) {
0981: String targetObject;
0982:
0983: if (isJPA) {
0984: // jpa/0h0a
0985:
0986: String targetTypeExt = getEntityTargetType()
0987: .getInstanceClassName();
0988:
0989: targetObject = "(" + targetTypeExt + ") child";
0990: } else
0991: targetObject = generateSuperGetter();
0992:
0993: String objThis = "(("
0994: + getRelatedType().getInstanceClassName()
0995: + ") " + dst + ")";
0996:
0997: out.println(generateSuperSetter(objThis, targetObject)
0998: + ";");
0999: }
1000: }
1001:
1002: // jpa/0o05
1003: // }
1004:
1005: // commented out: jpa/0s29
1006: // if (_targetLoadIndex == updateIndex) { // ejb/0h20
1007:
1008: // }
1009:
1010: /*
1011: if (_targetLoadIndex == updateIndex) {
1012: // ejb/0a06
1013: String value = generateGet(src);
1014: out.println(generateSet(dst, value) + ";");
1015: }
1016: */
1017: }
1018:
1019: /**
1020: * Updates the cached copy.
1021: */
1022: public void generateCopyMergeObject(JavaWriter out, String dst,
1023: String src, int updateIndex) throws IOException {
1024: if (getLoadGroupIndex() != updateIndex)
1025: return;
1026:
1027: if (!(getEntityTargetType() instanceof EntityType))
1028: return;
1029:
1030: String value = generateGet(src);
1031:
1032: out.println("if (" + value + " != null) {");
1033: out.pushDepth();
1034:
1035: if (!isCascade(CascadeType.MERGE)) {
1036: // jpa/0h08
1037: value = "("
1038: + getJavaTypeName()
1039: + ") aConn.mergeDetachedEntity((com.caucho.amber.entity.Entity) "
1040: + value + ", false)";
1041: } else {
1042: value = "(" + getJavaTypeName() + ") aConn.recursiveMerge("
1043: + value + ")";
1044: }
1045:
1046: out.println(generateSet(dst, value) + ";");
1047:
1048: out.popDepth();
1049: out.println("}");
1050: }
1051:
1052: /**
1053: * Checks entity-relationships from an object.
1054: */
1055: public void generateDumpRelationships(JavaWriter out,
1056: int updateIndex) throws IOException {
1057: // jpa/0o05
1058:
1059: if (getLoadGroupIndex() != updateIndex)
1060: return;
1061:
1062: if (!(getEntityTargetType() instanceof EntityType))
1063: return;
1064:
1065: out.println();
1066: out.println("thisRef = (com.caucho.amber.entity.Entity) "
1067: + generateSuperGetter() + ";");
1068:
1069: out.println();
1070: out.println("if (thisRef != null) {");
1071: out.pushDepth();
1072:
1073: String var = "__caucho_field_" + getName();
1074:
1075: String logMessage = "relationship from owning side - entity class: \" + this.getClass().getName() + \" PK: \" + __caucho_getPrimaryKey() + \" to object class: \" + thisRef.getClass().getName() + \" PK: \" + "
1076: + var;
1077:
1078: out.println("if (__caucho_session.isCacheEntity(thisRef)) {");
1079: out.pushDepth();
1080:
1081: out
1082: .println("Exception e = new IllegalStateException(\"amber dump relationship: inconsistent "
1083: + logMessage + ");");
1084:
1085: out
1086: .println("__caucho_log.log(java.util.logging.Level.FINEST, e.toString(), e);");
1087:
1088: out.popDepth();
1089: out.println("} else {");
1090: out.pushDepth();
1091:
1092: out
1093: .println("__caucho_log.log(java.util.logging.Level.FINEST, \"amber dump relationship: consistent "
1094: + logMessage + ");");
1095:
1096: out.popDepth();
1097: out.println("}");
1098:
1099: out.popDepth();
1100: out.println("}");
1101:
1102: }
1103:
1104: private String generateAccessor(String src, String var) {
1105: if (src.equals("super"))
1106: return var;
1107: else
1108: return "((" + getRelatedType().getInstanceClassName()
1109: + ") " + src + ")." + var;
1110: }
1111:
1112: /**
1113: * Generates the set property.
1114: */
1115: public void generateSetProperty(JavaWriter out) throws IOException {
1116: // ejb/06gc - updates with EJB 2.0
1117:
1118: Id id = getEntityTargetType().getId();
1119: String var = "__caucho_field_" + getName();
1120:
1121: String keyType = getEntityTargetType().getId()
1122: .getForeignTypeName();
1123:
1124: out.println();
1125: out.println("public void " + getSetterName() + "("
1126: + getJavaTypeName() + " v)");
1127: out.println("{");
1128: out.pushDepth();
1129:
1130: int group = getLoadGroupIndex() / 64;
1131: long loadMask = (1L << (getLoadGroupIndex() % 64));
1132: String loadVar = "__caucho_loadMask_" + group;
1133:
1134: if (_aliasField == null) {
1135: out.println("if ((" + loadVar + " & " + loadMask
1136: + "L) == 0 && __caucho_session != null) {");
1137: // ejb/0602
1138: out.println(" __caucho_load_select_" + group
1139: + "(__caucho_session);");
1140: out.println();
1141: // jpa/0j5f
1142: out
1143: .println(" if (__caucho_session.isActiveTransaction())");
1144: out
1145: .println(" __caucho_session.makeTransactional((com.caucho.amber.entity.Entity) this);");
1146: out.println("}");
1147:
1148: out.println();
1149: out.println("if (v == null) {");
1150: out.println(" if (" + var + " == null) {");
1151: out.println(" " + generateSuperSetter("null") + ";");
1152: out.println(" return;");
1153: out.println(" }");
1154:
1155: out.println();
1156: out.println(" " + var + " = null;");
1157: out.println("} else {");
1158: out.pushDepth();
1159: out.print(keyType + " key = ");
1160:
1161: RelatedType targetType = getEntityTargetType();
1162:
1163: if (targetType.isEJBProxy(getJavaTypeName())) {
1164: // To handle EJB local objects.
1165: out.print(id.generateGetProxyKey("v"));
1166: } else {
1167: String v = "(("
1168: + getEntityTargetType().getInstanceClassName()
1169: + ") v)";
1170:
1171: out.print(id.toObject(id.generateGetProperty(v)));
1172: }
1173:
1174: out.println(";");
1175:
1176: out.println();
1177: out.println("if (key.equals(" + var + ")) {");
1178: out.println(" " + generateSuperSetter("v") + ";");
1179: out.println(" return;");
1180: out.println("}");
1181:
1182: out.println();
1183: out.println(var + " = key;");
1184:
1185: out.popDepth();
1186: out.println("}");
1187:
1188: out.println();
1189:
1190: String dirtyVar = "__caucho_dirtyMask_" + (getIndex() / 64);
1191: long dirtyMask = (1L << (getIndex() % 64));
1192:
1193: // jpa/0o42: merge()
1194: out.println(dirtyVar + " |= " + dirtyMask + "L;");
1195:
1196: out.println(generateSuperSetter("v") + ";");
1197: out.println();
1198: out.println("if (__caucho_session != null) {");
1199: out.pushDepth();
1200:
1201: out.println(loadVar + " |= " + loadMask + "L;");
1202:
1203: out.println("try {");
1204: out.pushDepth();
1205:
1206: // XXX: jpa/0h27
1207: out.println("if (__caucho_state.isPersist())");
1208: out
1209: .println(" __caucho_cascadePrePersist(__caucho_session);");
1210:
1211: out.popDepth();
1212: out.println("} catch (RuntimeException e) {");
1213: out.println(" throw e;");
1214: out.println("} catch (Exception e) {");
1215: out
1216: .println(" throw new com.caucho.amber.AmberRuntimeException(e);");
1217: out.println("}");
1218:
1219: out
1220: .println("__caucho_session.update((com.caucho.amber.entity.Entity) this);");
1221:
1222: out
1223: .println("__caucho_session.addCompletion(__caucho_home.createManyToOneCompletion(\""
1224: + getName()
1225: + "\", (com.caucho.amber.entity.Entity) this, v));");
1226:
1227: out.println();
1228:
1229: out.popDepth();
1230: out.println("}");
1231: } else {
1232: out.println("if (__caucho_session != null)");
1233: out
1234: .println(" throw new IllegalStateException(\"aliased field cannot be set\");");
1235:
1236: out.println(generateSuperSetter("v") + ";");
1237: }
1238:
1239: out.popDepth();
1240: out.println("}");
1241: }
1242:
1243: /**
1244: * Generates the flush check for this child.
1245: */
1246: public boolean generateFlushCheck(JavaWriter out)
1247: throws IOException {
1248: // ejb/06bi
1249: if (!getRelatedType().getPersistenceUnit().isJPA())
1250: return false;
1251:
1252: String getter = generateSuperGetter();
1253:
1254: String dirtyVar = "__caucho_dirtyMask_" + (getIndex() / 64);
1255: long dirtyMask = (1L << (getIndex() % 64));
1256:
1257: out.println("if ((" + getter
1258: + " != null) && (__caucho_state.isPersist() || ("
1259: + dirtyVar + " & " + dirtyMask + ") != 0L)) {");
1260: out.pushDepth();
1261:
1262: String relatedEntity = "((com.caucho.amber.entity.Entity) "
1263: + getter + ")";
1264: out.println("com.caucho.amber.entity.EntityState otherState = "
1265: + relatedEntity + ".__caucho_getEntityState();");
1266:
1267: // jpa/0j5e as a negative test.
1268: out.println("if (" + relatedEntity
1269: + ".__caucho_getConnection() == null) {");
1270: out.pushDepth();
1271:
1272: // jpa/0j5c as a positive test.
1273: out
1274: .println("if (__caucho_state.isTransactional() && ! otherState.isManaged())");
1275:
1276: String errorString = ("(\"amber flush: unable to flush "
1277: + getRelatedType().getName()
1278: + "[\" + __caucho_getPrimaryKey() + \"] "
1279: + "with non-managed dependent relationship many-to-one to "
1280: + getEntityTargetType().getName()
1281: + ". Current entity state: \" + __caucho_state + \" " + "and parent entity state: \" + otherState)");
1282:
1283: out.println(" throw new IllegalStateException" + errorString
1284: + ";");
1285:
1286: out.popDepth();
1287: out.println("}");
1288:
1289: out.popDepth();
1290: out.println("}");
1291:
1292: return true;
1293: }
1294:
1295: /**
1296: * Generates the set clause.
1297: */
1298: public void generateSet(JavaWriter out, String pstmt, String index,
1299: String source) throws IOException {
1300: if (_aliasField != null)
1301: return;
1302:
1303: if (source == null) {
1304: throw new NullPointerException();
1305: }
1306:
1307: String var = "__caucho_field_" + getName();
1308:
1309: if (!source.equals("this") && !source.equals("super"))
1310: var = source + "." + var;
1311:
1312: if (!(isAbstract() && getRelatedType().getPersistenceUnit()
1313: .isJPA())) {
1314: // jpa/1004, ejb/06bi
1315: out.println("if (" + var + " != null) {");
1316: } else if (isCascade(CascadeType.PERSIST)) {
1317: // jpa/0h09
1318: out.println("if (" + var + " != null) {");
1319: } else {
1320: // jpa/0j58: avoids breaking FK constraints.
1321:
1322: // The "one" end in the many-to-one relationship.
1323: String amberVar = getFieldName();
1324:
1325: out.println("com.caucho.amber.entity.EntityState "
1326: + amberVar + "_state = (" + var + " == null) ? ");
1327: out
1328: .println("com.caucho.amber.entity.EntityState.TRANSIENT : ");
1329: out.println("((com.caucho.amber.entity.Entity) " + amberVar
1330: + ").");
1331: out.println("__caucho_getEntityState();");
1332:
1333: out.println("if (" + amberVar
1334: + "_state.isTransactional()) {");
1335: }
1336:
1337: out.pushDepth();
1338:
1339: Id id = getEntityTargetType().getId();
1340: ArrayList<IdField> keys = id.getKeys();
1341:
1342: if (keys.size() == 1) {
1343: IdField key = keys.get(0);
1344:
1345: key.getType().generateSet(out, pstmt, index,
1346: key.getType().generateCastFromObject(var));
1347: } else {
1348: for (int i = 0; i < keys.size(); i++) {
1349: IdField key = keys.get(i);
1350:
1351: key.getType().generateSet(out, pstmt, index,
1352: key.generateGetKeyProperty(var));
1353: }
1354: }
1355:
1356: out.popDepth();
1357: out.println("} else {");
1358: out.pushDepth();
1359:
1360: for (int i = 0; i < keys.size(); i++) {
1361: IdField key = keys.get(i);
1362:
1363: key.getType().generateSetNull(out, pstmt, index);
1364: }
1365:
1366: out.popDepth();
1367: out.println("}");
1368: }
1369:
1370: /**
1371: * Generates loading cache
1372: */
1373: public void generateUpdateFromObject(JavaWriter out, String obj)
1374: throws IOException {
1375: String var = "__caucho_field_" + getName();
1376:
1377: out.println(var + " = " + obj + "." + var + ";");
1378: }
1379:
1380: /**
1381: * Generates code for foreign entity create/delete
1382: */
1383: public void generateInvalidateForeign(JavaWriter out)
1384: throws IOException {
1385: out.println("if (\"" + _targetType.getTable().getName()
1386: + "\".equals(table)) {");
1387: out.pushDepth();
1388:
1389: String loadVar = "__caucho_loadMask_" + (_targetLoadIndex / 64);
1390:
1391: out.println(loadVar + " = 0L;");
1392: out.popDepth();
1393: out.println("}");
1394: }
1395:
1396: /**
1397: * Generates any pre-delete code
1398: */
1399: public void generatePreDelete(JavaWriter out) throws IOException {
1400: if (!isTargetCascadeDelete())
1401: return;
1402:
1403: String var = "caucho_field_" + getName();
1404:
1405: out.println(getJavaTypeName() + " " + var + " = "
1406: + getGetterName() + "();");
1407: }
1408:
1409: /**
1410: * Generates any pre-delete code
1411: */
1412: public void generatePostDelete(JavaWriter out) throws IOException {
1413: if (!isTargetCascadeDelete())
1414: return;
1415:
1416: String var = "caucho_field_" + getName();
1417:
1418: out.println("if (" + var + " != null) {");
1419: out.println(" try {");
1420: // out.println(" __caucho_session.delete(" + var + ");");
1421: out.println(" " + var + ".remove();");
1422: out.println(" } catch (Exception e) {");
1423: out
1424: .println(" throw com.caucho.amber.AmberRuntimeException.create(e);");
1425: out.println(" }");
1426: out.println("}");
1427: }
1428: }
|