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.OneToManyExpr;
034: import com.caucho.amber.expr.PathExpr;
035: import com.caucho.amber.query.QueryParser;
036: import com.caucho.amber.table.LinkColumns;
037: import com.caucho.amber.table.Table;
038: import com.caucho.amber.type.RelatedType;
039: import com.caucho.amber.type.Type;
040: import com.caucho.bytecode.JField;
041: import com.caucho.bytecode.JType;
042: import com.caucho.config.ConfigException;
043: import com.caucho.java.JavaWriter;
044: import com.caucho.log.Log;
045: import com.caucho.util.CharBuffer;
046: import com.caucho.util.L10N;
047:
048: import javax.persistence.CascadeType;
049: import java.io.IOException;
050: import java.util.ArrayList;
051: import java.util.Map;
052: import java.util.Set;
053: import java.util.logging.Logger;
054:
055: /**
056: * Represents a field to a collection of objects where the target
057: * hold a back-link to the source entity.
058: */
059: public class EntityOneToManyField extends CollectionField {
060: private static final L10N L = new L10N(EntityOneToManyField.class);
061: protected static final Logger log = Log
062: .open(EntityOneToManyField.class);
063:
064: private String _mapKey;
065:
066: private ArrayList<String> _orderByFields;
067: private ArrayList<Boolean> _orderByAscending;
068:
069: private EntityManyToOneField _sourceField;
070:
071: public EntityOneToManyField(RelatedType entityType, String name,
072: CascadeType[] cascadeTypes) throws ConfigException {
073: super (entityType, name, cascadeTypes);
074: }
075:
076: public EntityOneToManyField(RelatedType entityType, String name)
077: throws ConfigException {
078: this (entityType, name, null);
079: }
080:
081: public EntityOneToManyField(RelatedType entityType) {
082: super (entityType);
083: }
084:
085: /**
086: * Sets the order by.
087: */
088: public void setOrderBy(ArrayList<String> orderByFields,
089: ArrayList<Boolean> orderByAscending) {
090: _orderByFields = orderByFields;
091: _orderByAscending = orderByAscending;
092: }
093:
094: /**
095: * Returns the source type as
096: * entity or mapped-superclass.
097: */
098: public RelatedType getEntitySourceType() {
099: return (RelatedType) getSourceType();
100: }
101:
102: /**
103: * Returns the target type as
104: * entity or mapped-superclass.
105: */
106: public RelatedType getEntityTargetType() {
107: return (RelatedType) getTargetType();
108: }
109:
110: /**
111: * Returns the target type as entity.
112: */
113: public Type getTargetType() {
114: return _sourceField.getSourceType();
115: }
116:
117: /**
118: * Gets the source field.
119: */
120: public EntityManyToOneField getSourceField() {
121: return _sourceField;
122: }
123:
124: /**
125: * Sets the source field.
126: */
127: public void setSourceField(EntityManyToOneField sourceField) {
128: _sourceField = sourceField;
129: }
130:
131: /**
132: * Returns the link.
133: */
134: public LinkColumns getLinkColumns() {
135: return _sourceField.getLinkColumns();
136: }
137:
138: /**
139: * Gets the map key.
140: */
141: public String getMapKey() {
142: return _mapKey;
143: }
144:
145: /**
146: * Sets the map key.
147: */
148: public void setMapKey(String mapKey) {
149: _mapKey = mapKey;
150: }
151:
152: /**
153: * Initialize.
154: */
155: public void init() {
156: // jpa/0gg2
157: if (_sourceField == null) // || getLinkColumns() == null)
158: throw new IllegalStateException();
159: }
160:
161: /**
162: * Creates the expression for the field.
163: */
164: public AmberExpr createExpr(QueryParser parser, PathExpr parent) {
165: return new OneToManyExpr(parser, parent, getLinkColumns());
166: }
167:
168: /**
169: * Generates the (pre) cascade operation from
170: * parent to this child. This field will only
171: * be cascaded first if the operation can be
172: * performed with no risk to break FK constraints.
173: */
174: public void generatePreCascade(JavaWriter out, String aConn,
175: CascadeType cascadeType) throws IOException {
176: if (cascadeType == CascadeType.PERSIST)
177: return;
178:
179: generateInternalCascade(out, aConn, cascadeType);
180: }
181:
182: /**
183: * Generates the (post) cascade operation from
184: * parent to this child. This field will only
185: * be cascaded first if the operation can be
186: * performed with no risk to break FK constraints.
187: */
188: public void generatePostCascade(JavaWriter out, String aConn,
189: CascadeType cascadeType) throws IOException {
190: if (cascadeType != CascadeType.PERSIST)
191: return;
192:
193: generateInternalCascade(out, aConn, cascadeType);
194: }
195:
196: protected void generateInternalCascade(JavaWriter out,
197: String aConn, CascadeType cascadeType) throws IOException {
198: if (isCascade(cascadeType)) {
199:
200: String getter = "_caucho_field_" + getGetterName(); // generateSuperGetter();
201:
202: out.println("if (" + getter + " == null && "
203: + generateSuperGetter() + " != null)");
204: out.pushDepth();
205: out.println(getSetterName() + "(" + generateSuperGetter()
206: + ");");
207: out.popDepth();
208:
209: out.println();
210: out.println("if (" + getter + " != null) {");
211: out.pushDepth();
212:
213: out.print("for (Object o : " + getter);
214:
215: // jpa/0v04
216: if (getJavaType().isAssignableTo(Map.class))
217: out.print(".values()");
218:
219: out.println(") {");
220: out.pushDepth();
221:
222: // XXX
223: out.println("if (o == null)");
224: out.println(" continue;");
225:
226: if (_sourceField != null) {
227: String typeName = getEntityTargetType()
228: .getJavaTypeName();
229: String setter = _sourceField.getSetterName();
230: out.println("((" + typeName + ") o)." + setter
231: + "(this);");
232: }
233:
234: out.print(aConn + ".");
235:
236: switch (cascadeType) {
237: case PERSIST:
238: out.print("persistFromCascade");
239: break;
240:
241: case MERGE:
242: out.print("merge");
243: break;
244:
245: case REMOVE:
246: out.print("remove");
247: break;
248:
249: case REFRESH:
250: out.print("refresh");
251: break;
252: }
253:
254: out.println("(o);");
255:
256: out.popDepth();
257: out.println("}");
258:
259: out.popDepth();
260: out.println("}");
261: }
262: }
263:
264: /**
265: * Generates the set clause.
266: */
267: public void generateSet(JavaWriter out, String pstmt, String obj,
268: String index) throws IOException {
269: }
270:
271: /**
272: * Generates the select clause.
273: */
274: public String generateLoadSelect(String id) {
275: return null;
276: }
277:
278: /**
279: * Updates from the cached copy.
280: */
281: public void generateCopyLoadObject(JavaWriter out, String dst,
282: String src, int loadIndex) throws IOException {
283: }
284:
285: /**
286: * Generates the target select.
287: */
288: public String generateTargetSelect(String id) {
289: CharBuffer cb = CharBuffer.allocate();
290:
291: Id key = getEntityTargetType().getId();
292:
293: cb.append(key.generateSelect(id));
294:
295: String value = getEntityTargetType().generateLoadSelect(id);
296:
297: if (cb.length() > 0 && value.length() > 0)
298: cb.append(", ");
299:
300: cb.append(value);
301:
302: return cb.close();
303: }
304:
305: /**
306: * Generates the set property.
307: */
308: public void generateGetProperty(JavaWriter out) throws IOException {
309: String var = "_caucho_field_" + getGetterName();
310:
311: boolean isSet = getJavaType().isAssignableTo(Set.class);
312: boolean isMap = false;
313: if (!isSet) {
314: isMap = getJavaType().isAssignableTo(Map.class);
315: }
316:
317: JType type = getJavaType();
318: JType[] paramArgs = type.getActualTypeArguments();
319: JType param = paramArgs.length > 0 ? paramArgs[0] : null;
320: JType param2 = paramArgs.length > 1 ? paramArgs[1] : null;
321:
322: out.print("protected transient ");
323:
324: String collectionImpl;
325:
326: if (isSet)
327: collectionImpl = "com.caucho.amber.collection.SetImpl";
328: else if (isMap)
329: collectionImpl = "com.caucho.amber.collection.MapImpl";
330: else
331: collectionImpl = "com.caucho.amber.collection.CollectionImpl";
332:
333: out.print(collectionImpl);
334:
335: if (param != null) {
336: out.print("<");
337: out.print(param.getPrintName());
338: if (isMap) {
339: if (param2 != null) {
340: out.print(", ");
341: out.print(param2.getPrintName());
342: }
343: }
344: out.print(">");
345: }
346:
347: out.println(" " + var + ";");
348:
349: out.println();
350: out.println("public " + getJavaTypeName() + " "
351: + getGetterName() + "()");
352: out.println("{");
353: out.pushDepth();
354:
355: out.println("if (" + var + " != null) {");
356: out.pushDepth();
357:
358: out.println("if (__caucho_state.isPersist()) {");
359: out.pushDepth();
360:
361: out.println(var + ".setSession(__caucho_session);");
362: out.println("return " + var + ";");
363:
364: out.popDepth();
365: out.println("}");
366: out.println();
367:
368: // jpa/1621
369: out.println("if (" + var + ".getSession() != null");
370: out.println(" && " + var
371: + ".getSession() == __caucho_session)");
372: out.println(" return " + var + ";");
373:
374: out.popDepth();
375: out.println("}");
376:
377: out.println();
378: out.println("com.caucho.amber.AmberQuery query = null;");
379:
380: String newEmptyCollection = "new " + collectionImpl;
381:
382: if (param != null) {
383: newEmptyCollection += "<" + param.getPrintName();
384: if (isMap) {
385: newEmptyCollection += ", ";
386: newEmptyCollection += param2.getPrintName();
387: }
388: newEmptyCollection += ">";
389: }
390:
391: newEmptyCollection += "(query";
392: if (isMap) {
393: // jpa/0v00
394: newEmptyCollection += ","
395: + getEntityTargetType().getBeanClass().getName();
396: newEmptyCollection += ".class.getDeclaredMethod(\"";
397:
398: String getterMapKey = getMapKey();
399:
400: // jpa/0j63
401: if (getterMapKey == null) {
402: getterMapKey = getEntityTargetType().getId()
403: .generateGetProperty("this");
404: } else {
405: getterMapKey = "get"
406: + Character.toUpperCase(getterMapKey.charAt(0))
407: + getterMapKey.substring(1);
408: }
409:
410: newEmptyCollection += getterMapKey; // "getId");
411: newEmptyCollection += "\", (Class []) null)";
412: }
413: newEmptyCollection += ")";
414:
415: out.println();
416: out.println("try {");
417: out.pushDepth();
418:
419: out.println("if (__caucho_session == null) {");
420: out.pushDepth();
421:
422: /*
423: out.println("if (" + var + " == null)");
424: out.println(" " + var + " = " + newEmptyCollection + ";");
425:
426: // if (! isAbstract())
427: out.println();
428: out.println("return " + var + ";");
429: */
430:
431: out.println("return " + generateSuperGetter() + ";");
432:
433: out.popDepth();
434: out.println("}");
435:
436: out.println();
437: out.print("String sql=\"");
438:
439: out.print("SELECT c");
440: out.print(" FROM " + getEntitySourceType().getName() + " o,");
441: out.print(" o." + getName() + " c");
442: out.print(" WHERE ");
443: out.print(getEntitySourceType().getId().generateRawWhere("o"));
444:
445: if (_orderByFields != null) {
446: out.print(" ORDER BY ");
447:
448: for (int i = 0; i < _orderByFields.size(); i++) {
449: if (i != 0)
450: out.print(", ");
451:
452: out.print("c." + _orderByFields.get(i));
453: if (Boolean.FALSE.equals(_orderByAscending.get(i)))
454: out.print(" DESC");
455: }
456: }
457:
458: out.println("\";");
459: out.println("query = __caucho_session.prepareQuery(sql);");
460:
461: out.println("int index = 1;");
462: getEntitySourceType().getId().generateSet(out, "query",
463: "index", "this");
464:
465: // Ex: _caucho_getChildren = new com.caucho.amber.collection.CollectionImpl
466: out.print(var);
467: out.print(" = " + newEmptyCollection + ";");
468:
469: /*
470: out.pushDepth();
471:
472: //generateAdd(out);
473: //generateRemove(out);
474: //generateClear(out);
475: // generateSize(out);
476:
477: out.popDepth();
478: out.println("};");
479: */
480:
481: // ejb/0aj2
482: if (getEntitySourceType().getPersistenceUnit().isJPA()) {
483: // jpa/0l43
484: out.println();
485: out.print("for (Object o : " + var);
486:
487: // jpa/0v04
488: if (getJavaType().isAssignableTo(Map.class))
489: out.print(".values()");
490:
491: out.println(")");
492: out
493: .println(" __caucho_session.makeTransactional((com.caucho.amber.entity.Entity) o);");
494: }
495:
496: // jpa/0j70
497: out.println(generateSuperSetter(var) + ";");
498:
499: out.println();
500: out.println("return " + var + ";");
501:
502: out.popDepth();
503: out.println("} catch (Exception e) {");
504: out
505: .println(" throw com.caucho.amber.AmberRuntimeException.create(e);");
506: out.println("}");
507:
508: out.popDepth();
509: out.println("}");
510: }
511:
512: /**
513: * Generates the size method.
514: */
515: private void generateSize(JavaWriter out) throws IOException {
516: out.println("public int size()");
517: out.println("{");
518: out.pushDepth();
519:
520: out.println("if (__caucho_session == null || isValid())");
521: out.println(" return super.size();");
522:
523: out.println("try {");
524: out.pushDepth();
525:
526: out.println("__caucho_session.flushNoChecks();");
527:
528: out.print("String sql=\"");
529:
530: out.print("SELECT count(*) FROM ");
531: out.print(getEntitySourceType().getName());
532: out.print(" AS o ");
533:
534: out.print(" WHERE ");
535:
536: // getKeyColumn().generateRawMatchArgWhere("o");
537:
538: ArrayList<IdField> keys = getEntitySourceType().getId()
539: .getKeys();
540: for (int i = 0; i < keys.size(); i++) {
541: if (i != 0)
542: out.print(" AND ");
543:
544: out.print("o." + keys.get(i).getName());
545: out.print("=?");
546: }
547:
548: out.println("\";");
549: out.println("com.caucho.amber.AmberQuery query;");
550: out.println("query = __caucho_session.prepareQuery(sql);");
551:
552: out.println("int index = 1;");
553:
554: // ejb/06h0
555: getEntitySourceType().getId().generateSet(out, "query",
556: "index",
557: getEntitySourceType().getInstanceClassName() + ".this"); // "__ResinExt.this");
558:
559: out.println("java.sql.ResultSet rs = query.executeQuery();");
560:
561: out.println("if (rs.next())");
562: out.println(" return rs.getInt(1);");
563: out.println("else");
564: out.println(" return 0;");
565:
566: out.popDepth();
567: out.println("} catch (java.sql.SQLException e) {");
568: out
569: .println(" throw com.caucho.amber.AmberRuntimeException.create(e);");
570: out.println("}");
571:
572: out.popDepth();
573: out.println("}");
574: }
575:
576: /**
577: * Generates the set property.
578: */
579: public void generateSetProperty(JavaWriter out) throws IOException {
580: // commented out: jpa/0s2d
581: // JMethod setter = getSetterMethod();
582: //
583: // if (setter == null)
584: // return;
585: //
586: // JClass []paramTypes = setter.getParameterTypes();
587:
588: JType type;
589:
590: if (!getEntitySourceType().isFieldAccess()) {
591: type = getGetterMethod().getGenericReturnType();
592: } else {
593: JField field = RelatedType.getField(getBeanClass(),
594: getName());
595: type = field.getGenericType();
596: }
597:
598: out.println();
599: // commented out: jpa/0s2d
600: // out.print("public void " + setter.getName() + "(");
601: // out.print(getJavaTypeName() + " value)");
602: out.print("public void " + getSetterName() + "(");
603: out.print(type.getName() + " value)");
604: out.println("{");
605: out.pushDepth();
606:
607: // out.println("if (" + generateSuperGetter() + " == value)");
608: // out.println(" return;");
609: // out.println();
610:
611: //
612: // jpa/0j57 needs to generate the following snippet:
613: //
614: // _caucho___caucho_get_xAnnualReviews
615: // = new com.caucho.amber.collection.CollectionImpl<qa.XAnnualReview>(__caucho_session, null);
616: // _caucho___caucho_get_xAnnualReviews.addAll(0, value);
617: //
618: // jpa/0j57:
619:
620: out.println("if (__caucho_session == null) {");
621: out.pushDepth();
622: out.println(generateSuperSetter("value") + ";");
623: out.popDepth();
624: out.println("} else {");
625: out.pushDepth();
626:
627: out.println("try {");
628: out.pushDepth();
629:
630: String var = "_caucho_field_" + getGetterName();
631:
632: out.print(var + " = new ");
633:
634: type = getJavaType();
635:
636: boolean isSet = type.isAssignableTo(Set.class);
637: boolean isMap = false;
638: if (!isSet) {
639: isMap = type.isAssignableTo(Map.class);
640: }
641:
642: JType[] paramArgs = type.getActualTypeArguments();
643: JType param = paramArgs.length > 0 ? paramArgs[0] : null;
644: JType param2 = paramArgs.length > 1 ? paramArgs[1] : null;
645:
646: String collectionImpl;
647:
648: if (isSet)
649: collectionImpl = "com.caucho.amber.collection.SetImpl";
650: else if (isMap)
651: collectionImpl = "com.caucho.amber.collection.MapImpl";
652: else
653: collectionImpl = "com.caucho.amber.collection.CollectionImpl";
654:
655: out.print(collectionImpl);
656:
657: if (param != null) {
658: out.print("<");
659: out.print(param.getPrintName());
660: if (isMap) {
661: if (param2 != null) {
662: out.print(", ");
663: out.print(param2.getPrintName());
664: }
665: }
666: out.print(">");
667: }
668:
669: out.print("(__caucho_session, null");
670: if (isMap) {
671: out.print(", ");
672: out.print(getEntityTargetType().getBeanClass().getName());
673: out.print(".class.getDeclaredMethod(\"");
674:
675: String getterMapKey = getMapKey();
676:
677: // jpa/0j63
678: if (getterMapKey == null) {
679: getterMapKey = getEntityTargetType().getId().getKey()
680: .getGetterName();
681: } else {
682: getterMapKey = "get"
683: + Character.toUpperCase(getterMapKey.charAt(0))
684: + getterMapKey.substring(1);
685: }
686:
687: out.print(getterMapKey); // "getId");
688: out.print("\")");
689: }
690: out.println(");");
691:
692: out.print(var + ".");
693:
694: if (isMap)
695: out.println("putAll(value);");
696: else if (isSet)
697: out.println("addAll(value);");
698: else
699: out.println("addAll(0, value);");
700:
701: out.popDepth();
702: out.println("} catch(Exception e) {");
703: out
704: .println(" throw com.caucho.amber.AmberRuntimeException.create(e);");
705: out.println("}");
706:
707: out.popDepth();
708: out.println("}");
709:
710: out.popDepth();
711: out.println("}");
712: }
713:
714: /**
715: * Generates code for foreign entity create/delete
716: */
717: public void generateInvalidateForeign(JavaWriter out)
718: throws IOException {
719: // XXX: jpa/0gg2
720: if (getEntitySourceType().getPersistenceUnit().isJPA())
721: return;
722:
723: Table table = getLinkColumns().getSourceTable();
724:
725: out.println("if (\"" + table.getName() + "\".equals(table)) {");
726: out.pushDepth();
727:
728: String var = "_caucho_field_" + getGetterName();
729:
730: out.println("if (" + var + " != null)");
731: out.println(" " + var + ".update();");
732: out.popDepth();
733: out.println("}");
734: }
735:
736: /**
737: * Generates the expire code
738: *
739: * ejb/06hi
740: */
741: public void generateExpire(JavaWriter out) throws IOException {
742: String var = "_caucho_field_" + getGetterName();
743:
744: out.println(var + " = null;");
745: }
746: }
|