001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.amber.field;
031:
032: import com.caucho.amber.expr.AmberExpr;
033: import com.caucho.amber.expr.DependentEntityOneToOneExpr;
034: import com.caucho.amber.expr.PathExpr;
035: import com.caucho.amber.manager.AmberPersistenceUnit;
036: import com.caucho.amber.query.QueryParser;
037: import com.caucho.amber.table.*;
038: import com.caucho.amber.type.*;
039: import com.caucho.config.ConfigException;
040: import com.caucho.java.JavaWriter;
041: import com.caucho.log.Log;
042: import com.caucho.util.CharBuffer;
043: import com.caucho.util.L10N;
044:
045: import java.io.IOException;
046: import java.util.ArrayList;
047: import java.util.HashSet;
048: import java.util.logging.Logger;
049:
050: import javax.persistence.CascadeType;
051:
052: /**
053: * Represents the dependent side of a one-to-one bidirectional link.
054: */
055: public class DependentEntityOneToOneField extends CascadableField {
056: private static final L10N L = new L10N(
057: DependentEntityOneToOneField.class);
058: protected static final Logger log = Log
059: .open(DependentEntityOneToOneField.class);
060:
061: private EntityManyToOneField _targetField;
062: private long _targetLoadIndex;
063: private boolean _isCascadeDelete;
064:
065: public DependentEntityOneToOneField(RelatedType relatedType,
066: String name) throws ConfigException {
067: super (relatedType, name, null);
068: }
069:
070: public DependentEntityOneToOneField(RelatedType relatedType,
071: String name, CascadeType[] cascadeTypes)
072: throws ConfigException {
073: super (relatedType, name, cascadeTypes);
074: }
075:
076: /**
077: * Sets the target field.
078: */
079: public void setTargetField(EntityManyToOneField targetField) {
080: _targetField = targetField;
081: }
082:
083: /**
084: * Sets the target field.
085: */
086: public EntityManyToOneField getTargetField() {
087: return _targetField;
088: }
089:
090: /**
091: * Gets the target load index.
092: */
093: public long getTargetLoadIndex() {
094: return _targetLoadIndex;
095: }
096:
097: /**
098: * Returns the source type as
099: * entity or mapped-superclass.
100: */
101: public RelatedType getEntitySourceType() {
102: return (RelatedType) getSourceType();
103: }
104:
105: /**
106: * Returns the target type as
107: * entity or mapped-superclass.
108: */
109: public RelatedType getEntityTargetType() {
110: return (RelatedType) _targetField.getSourceType();
111: }
112:
113: /**
114: * Returns the target type.
115: */
116: public Type getType() {
117: return getEntityTargetType();
118: }
119:
120: /**
121: * Returns the foreign type.
122: */
123: public String getForeignTypeName() {
124: //return ((KeyColumn) getColumn()).getType().getForeignTypeName();
125: return getEntityTargetType().getForeignTypeName();
126: }
127:
128: /**
129: * Sets the column.
130: */
131: public void setColumn(Column column) {
132: throw new IllegalStateException();
133: }
134:
135: /**
136: * Sets the cascade-delete property.
137: */
138: public void setCascadeDelete(boolean isCascadeDelete) {
139: _isCascadeDelete = isCascadeDelete;
140: }
141:
142: /**
143: * Returns the cascade-delete property.
144: */
145: public boolean isCascadeDelete() {
146: return _isCascadeDelete;
147: }
148:
149: public void init() throws ConfigException {
150: super .init();
151:
152: _targetLoadIndex = getEntitySourceType().nextLoadGroupIndex();
153: }
154:
155: /**
156: * Creates the expression for the field.
157: */
158: public AmberExpr createExpr(QueryParser parser, PathExpr parent) {
159: return new DependentEntityOneToOneExpr(parent, _targetField
160: .getLinkColumns());
161: }
162:
163: /**
164: * Gets the column corresponding to the target field.
165: */
166: public ForeignColumn getColumn(IdField targetField) {
167: /*
168: EntityColumn entityColumn = (EntityColumn) getColumn();
169:
170: ArrayList<ForeignColumn> columns = entityColumn.getColumns();
171:
172: Id id = getEntityTargetType().getId();
173: ArrayList<IdField> keys = id.getKeys();
174:
175: for (int i = 0; i < keys.size(); i++ ){
176: if (keys.get(i) == targetField)
177: return columns.get(i);
178: }
179: */
180:
181: return null;
182: }
183:
184: /**
185: * Generates the flush check for this child.
186: */
187: public boolean generateFlushCheck(JavaWriter out)
188: throws IOException {
189: // ejb/06bi
190: if (!getEntitySourceType().getPersistenceUnit().isJPA())
191: return false;
192:
193: String getter = generateSuperGetter();
194:
195: out.println("if (" + getter + " != null) {");
196: out.pushDepth();
197: out
198: .println("com.caucho.amber.entity.EntityState state = ((com.caucho.amber.entity.Entity) "
199: + getter + ").__caucho_getEntityState();");
200:
201: // jpa/0s2d
202: out
203: .println("if (__caucho_state.isTransactional() && ! state.isManaged())");
204: String errorString = ("(\"amber flush: unable to flush "
205: + getEntitySourceType().getName()
206: + "[\" + __caucho_getPrimaryKey() + \"] "
207: + "with non-managed relationship one-to-one to "
208: + getEntityTargetType().getName() + " with state='\" + __caucho_state + \"'\")");
209:
210: // jpa/0o37 (tck)
211: out.println(" throw new IllegalStateException" + errorString
212: + ";");
213: out.popDepth();
214: out.println("}");
215:
216: return true;
217: }
218:
219: /**
220: * Generates any prologue.
221: */
222: public void generatePrologue(JavaWriter out,
223: HashSet<Object> completedSet) throws IOException {
224: super .generatePrologue(out, completedSet);
225:
226: out.println();
227:
228: Id id = getEntityTargetType().getId();
229:
230: id.generatePrologue(out, completedSet, getName());
231: }
232:
233: /**
234: * Generates the linking for a join
235: */
236: public void generateJoin(CharBuffer cb, String sourceTable,
237: String targetTable) {
238: LinkColumns linkColumns = _targetField.getLinkColumns();
239:
240: cb.append(linkColumns.generateJoin(sourceTable, targetTable));
241: }
242:
243: /**
244: * Generates loading code
245: */
246: public int generateLoad(JavaWriter out, String rs, String indexVar,
247: int index) throws IOException {
248: if (isLazy()) {
249: out.println(generateSuperSetter("null") + ";");
250:
251: String loadVar = "__caucho_loadMask_"
252: + (_targetLoadIndex / 64);
253: long loadMask = (1L << _targetLoadIndex);
254:
255: out.println(loadVar + " &= ~" + loadMask + "L;");
256: }
257:
258: return index;
259: }
260:
261: /**
262: * Generates loading code after the basic fields.
263: */
264: public int generatePostLoadSelect(JavaWriter out, int index)
265: throws IOException {
266: if (!isLazy()) {
267: long group = _targetLoadIndex / 64;
268: long mask = (1L << (_targetLoadIndex % 64));
269: String loadVar = "__caucho_loadMask_" + group;
270:
271: // out.println("if ((" + loadVar + " & " + mask + "L) == 0) {");
272: // out.pushDepth();
273: // out.println(loadVar + " |= " + mask + "L;");
274:
275: String javaType = getJavaTypeName();
276:
277: String indexS = "_" + group + "_" + index;
278:
279: // generateLoadProperty(out, indexS, "aConn");
280:
281: out.println(getGetterName() + "();");
282:
283: // out.popDepth();
284: // out.println("}");
285: }
286:
287: return ++index;
288: }
289:
290: /**
291: * Generates the set property.
292: */
293: public void generateGetProperty(JavaWriter out) throws IOException {
294: String loadVar = "__caucho_loadMask_" + (_targetLoadIndex / 64);
295: long loadMask = 1L << (_targetLoadIndex % 64);
296:
297: String index = "_" + (_targetLoadIndex / 64);
298: index += "_" + (1L << (_targetLoadIndex % 64));
299:
300: String javaType = getJavaTypeName();
301:
302: out.println();
303: out
304: .println("public " + javaType + " " + getGetterName()
305: + "()");
306: out.println("{");
307: out.pushDepth();
308:
309: // jpa/0h29
310: out.println("if (__caucho_session != null");
311: out
312: .println(" && __caucho_state != com.caucho.amber.entity.EntityState.P_DELETED");
313: out.println(" && (" + loadVar + " & " + loadMask
314: + "L) == 0) {");
315: out.pushDepth();
316: out.println("__caucho_load_select_" + getLoadGroupIndex()
317: + "(__caucho_session);");
318: out.println(loadVar + " |= " + loadMask + "L;");
319:
320: generateLoadProperty(out, index, "__caucho_session");
321:
322: /*
323: out.print(javaType + " v = (" + javaType + ") __caucho_session.loadProxy(\"" + getEntityTargetType().getName() + "\", ");
324: out.println("__caucho_field_" + getName() + ", true);");
325: */
326:
327: out.println("return v" + index + ";");
328: out.popDepth();
329: out.println("}");
330: out.println("else {");
331: out.println(" return " + generateSuperGetter() + ";");
332: out.println("}");
333:
334: out.popDepth();
335: out.println("}");
336: }
337:
338: /**
339: * Generates the set property.
340: */
341: public void generateLoadProperty(JavaWriter out, String index,
342: String session) throws IOException {
343: String javaType = getJavaTypeName();
344:
345: out.println(javaType + " v" + index + " = null;");
346:
347: out.println("try {");
348: out.pushDepth();
349:
350: out.print("String sql" + index + " = \"");
351: out.print("SELECT o." + getName() + " FROM "
352: + getEntitySourceType().getName() + " o" + " WHERE ");
353:
354: ArrayList<IdField> sourceKeys = getEntitySourceType().getId()
355: .getKeys();
356: for (int i = 0; i < sourceKeys.size(); i++) {
357: if (i != 0)
358: out.print(" and ");
359:
360: IdField key = sourceKeys.get(i);
361:
362: out.print("o." + key.getName() + "=?");
363: }
364: out.println("\";");
365:
366: out.println("com.caucho.amber.AmberQuery query" + index + " = "
367: + session + ".prepareQuery(sql" + index + ");");
368:
369: out.println("int index" + index + " = 1;");
370:
371: getEntitySourceType().getId().generateSet(out, "query" + index,
372: "index" + index, "super");
373:
374: boolean isJPA = getEntitySourceType().getPersistenceUnit()
375: .isJPA();
376:
377: if (isJPA) {
378: out.println("v" + index + " = (" + javaType + ") query"
379: + index + ".getSingleResult();");
380: } else {
381: // ejb/06hj
382: out
383: .println("com.caucho.amber.entity.Entity e = (com.caucho.amber.entity.Entity) query"
384: + index + ".getSingleResult();");
385: out
386: .println("v"
387: + index
388: + " = ("
389: + javaType
390: + ") __caucho_session.loadProxy(e.__caucho_getEntityType(), e.__caucho_getPrimaryKey());");
391: }
392:
393: out.popDepth();
394: out.println("} catch (java.sql.SQLException e) {");
395: out.println(" throw new RuntimeException(e);");
396: out.println("}");
397:
398: out.println(generateSuperSetter("v" + index) + ";");
399: }
400:
401: /**
402: * Updates the cached copy.
403: */
404: public void generateCopyUpdateObject(JavaWriter out, String dst,
405: String src, int updateIndex) throws IOException {
406: // jpa/0ge4
407:
408: /* XXX: should not be necessary to update the cache item from the
409: dependent side.
410:
411: if (getIndex() == updateIndex) {
412: String value = generateGet(src);
413:
414: value = "(" + getEntityTargetType().getInstanceClassName() + ") aConn.getEntity((com.caucho.amber.entity.Entity) " + value + ")";
415:
416: out.println(generateSet(dst, value) + ";");
417: }
418: */
419: }
420:
421: /**
422: * Updates the cached copy.
423: */
424: public void generateCopyLoadObject(JavaWriter out, String dst,
425: String src, int updateIndex) throws IOException {
426: if (getLoadGroupIndex() == updateIndex) {
427: // jpa/0o08, jpa/0o04
428: if (dst.equals("cacheEntity") || dst.equals("super"))
429: return;
430:
431: String value = generateGet(src);
432:
433: out.println("child = " + value + ";");
434:
435: boolean isJPA = getEntitySourceType().getPersistenceUnit()
436: .isJPA();
437:
438: if (isJPA && !dst.equals("item")) {
439: // jpa/0o42
440: out.println("if (isFullMerge)");
441: out.println(" child = " + value + ";");
442: out.println("else {");
443: out.pushDepth();
444: }
445:
446: out.println("if (child != null) {");
447: out.pushDepth();
448:
449: // jpa/0ge4
450: String targetTypeName = getJavaTypeName(); // getEntityTargetType().getInstanceClassName();
451:
452: // jpa/0l42
453: out
454: .println("com.caucho.amber.entity.Entity newChild = aConn.addNewEntity(child.getClass(), ((com.caucho.amber.entity.Entity) child).__caucho_getPrimaryKey());");
455:
456: out.println("if (newChild == null) {");
457: out.pushDepth();
458:
459: value = "aConn.getEntity((com.caucho.amber.entity.Entity) child)";
460:
461: out.println("newChild = " + value + ";");
462:
463: out.popDepth();
464: out.println("} else {");
465: out.pushDepth();
466:
467: out
468: .println("((com.caucho.amber.entity.Entity) child).__caucho_copyTo(newChild, aConn);");
469:
470: out.popDepth();
471: out.println("}");
472:
473: out.println("child = newChild;");
474:
475: out.popDepth();
476: out.println("}");
477:
478: if (isJPA && !dst.equals("item")) {
479: // jpa/0o42
480: out.popDepth();
481: out.println("}");
482: }
483:
484: value = "(" + targetTypeName + ") child";
485:
486: out.println(generateSet(dst, value) + ";");
487: }
488: }
489:
490: /**
491: * Updates the cached copy.
492: */
493: public void generateCopyMergeObject(JavaWriter out, String dst,
494: String src, int updateIndex) throws IOException {
495: if (getLoadGroupIndex() != updateIndex)
496: return;
497:
498: if (!(getEntityTargetType() instanceof EntityType))
499: return;
500:
501: String value = generateGet(src);
502:
503: out.println("if (" + value + " != null) {");
504: out.pushDepth();
505:
506: if (!isCascade(CascadeType.MERGE)) {
507: value = "("
508: + getJavaTypeName()
509: + ") aConn.mergeDetachedEntity((com.caucho.amber.entity.Entity) "
510: + value + ", false)";
511: } else {
512: value = "(" + getJavaTypeName() + ") aConn.recursiveMerge("
513: + value + ")";
514: }
515:
516: out.println(generateSet(dst, value) + ";");
517:
518: out.popDepth();
519: out.println("}");
520: }
521:
522: /**
523: * Checks entity-relationships from an object.
524: */
525: public void generateDumpRelationships(JavaWriter out,
526: int updateIndex) throws IOException {
527: // jpa/0o05
528:
529: if (getLoadGroupIndex() != updateIndex)
530: return;
531:
532: if (!(getEntityTargetType() instanceof EntityType))
533: return;
534:
535: out.println();
536: out.println("thisRef = (com.caucho.amber.entity.Entity) "
537: + generateSuperGetter() + ";");
538:
539: out.println();
540: out.println("if (thisRef != null) {");
541: out.pushDepth();
542:
543: String var = "thisRef.__caucho_getPrimaryKey()";
544:
545: String logMessage = "relationship from dependent side - entity class: \" + this.getClass().getName() + \" PK: \" + __caucho_getPrimaryKey() + \" to object class: \" + thisRef.getClass().getName() + \" PK: \" + "
546: + var;
547:
548: out.println("if (__caucho_session.isCacheEntity(thisRef)) {");
549: out.pushDepth();
550:
551: out
552: .println("Exception e = new IllegalStateException(\"amber dump relationship: inconsistent "
553: + logMessage + ");");
554:
555: out
556: .println("__caucho_log.log(java.util.logging.Level.FINEST, e.toString(), e);");
557:
558: out.popDepth();
559: out.println("} else {");
560: out.pushDepth();
561:
562: out
563: .println("__caucho_log.log(java.util.logging.Level.FINEST, \"amber dump relationship: consistent "
564: + logMessage + ");");
565:
566: out.popDepth();
567: out.println("}");
568:
569: out.popDepth();
570: out.println("}");
571:
572: }
573:
574: /**
575: * Generates the set property.
576: */
577: public void generateSetProperty(JavaWriter out) throws IOException {
578: Id id = getEntityTargetType().getId();
579:
580: String keyType = getEntityTargetType().getId()
581: .getForeignTypeName();
582:
583: out.println();
584: out.println("public void " + getSetterName() + "("
585: + getJavaTypeName() + " v)");
586: out.println("{");
587: out.pushDepth();
588:
589: out.println(generateSuperSetter("v") + ";");
590: out.println("if (__caucho_session != null) {");
591: out.pushDepth();
592:
593: out.println("try {");
594: out.pushDepth();
595:
596: // XXX: jpa/0h27
597: out.println("if (__caucho_state.isPersist())");
598: out.println(" __caucho_cascadePrePersist(__caucho_session);");
599:
600: out.popDepth();
601: out.println("} catch (RuntimeException e) {");
602: out.println(" throw e;");
603: out.println("} catch (Exception e) {");
604: out
605: .println(" throw new com.caucho.amber.AmberRuntimeException(e);");
606: out.println("}");
607:
608: String updateVar = "__caucho_updateMask_"
609: + (_targetLoadIndex / 64);
610: long updateMask = (1L << _targetLoadIndex);
611:
612: out.println(updateVar + " |= " + updateMask + "L;");
613: out.println("__caucho_session.update(this);");
614: out.popDepth();
615: out.println("}");
616:
617: out.popDepth();
618: out.println("}");
619: }
620:
621: /**
622: * Generates the set clause.
623: */
624: public void generateSet(JavaWriter out, String pstmt, String index)
625: throws IOException {
626: }
627:
628: /**
629: * Generates loading cache
630: */
631: public void generateUpdateFromObject(JavaWriter out, String obj)
632: throws IOException {
633: }
634:
635: /**
636: * Generates code for foreign entity create/delete
637: */
638: public void generateInvalidateForeign(JavaWriter out)
639: throws IOException {
640: // Table table = getEntityTargetType().getTable();
641:
642: AmberPersistenceUnit persistenceUnit = getSourceType()
643: .getPersistenceUnit();
644:
645: Table table;
646:
647: if (persistenceUnit.isJPA()) {
648: String className = getJavaType().getName();
649: EntityType entity = persistenceUnit
650: .getEntityType(className);
651:
652: // jpa/0ge4
653: table = entity.getTable();
654: } else {
655: // ejb/0691
656: table = getEntityTargetType().getTable();
657: }
658:
659: out.println("if (\"" + table.getName() + "\".equals(table)) {");
660: out.pushDepth();
661: String loadVar = "__caucho_loadMask_" + (_targetLoadIndex / 64);
662: out.println(loadVar + " = 0;");
663: out.popDepth();
664: out.println("}");
665: }
666:
667: public void generatePreCascade(JavaWriter out, String aConn,
668: CascadeType cascadeType) throws IOException {
669: if (cascadeType == CascadeType.PERSIST)
670: return;
671:
672: // jpa/0o33
673: generateInternalCascade(out, aConn, cascadeType);
674: }
675:
676: public void generatePostCascade(JavaWriter out, String aConn,
677: CascadeType cascadeType) throws IOException {
678: if (cascadeType != CascadeType.PERSIST)
679: return;
680:
681: // jpa/0o33
682: generateInternalCascade(out, aConn, cascadeType);
683: }
684: }
|