001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.devtools.jpa.eclipselink.conv.parser.helper.resolver; 017 018import japa.parser.ast.ImportDeclaration; 019import japa.parser.ast.expr.ArrayInitializerExpr; 020import japa.parser.ast.expr.BooleanLiteralExpr; 021import japa.parser.ast.expr.Expression; 022import japa.parser.ast.expr.MemberValuePair; 023import japa.parser.ast.expr.NameExpr; 024import japa.parser.ast.expr.NormalAnnotationExpr; 025import japa.parser.ast.expr.QualifiedNameExpr; 026import japa.parser.ast.expr.StringLiteralExpr; 027import org.apache.commons.lang.ClassUtils; 028import org.apache.commons.lang.StringUtils; 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.apache.ojb.broker.metadata.ClassDescriptor; 032import org.apache.ojb.broker.metadata.CollectionDescriptor; 033import org.apache.ojb.broker.metadata.DescriptorRepository; 034import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor; 035import org.kuali.rice.devtools.jpa.eclipselink.conv.ojb.OjbUtil; 036import org.kuali.rice.devtools.jpa.eclipselink.conv.parser.helper.NodeData; 037 038import java.util.ArrayList; 039import java.util.Collection; 040import java.util.Collections; 041import java.util.List; 042 043public class OneToManyResolver extends AbstractMappedFieldResolver { 044 private static final Log LOG = LogFactory.getLog(OneToManyResolver.class); 045 046 public static final String PACKAGE = "javax.persistence"; 047 public static final String SIMPLE_NAME = "OneToMany"; 048 049 public OneToManyResolver(Collection<DescriptorRepository> descriptorRepositories) { 050 super(descriptorRepositories); 051 } 052 @Override 053 public String getFullyQualifiedName() { 054 return PACKAGE + "." + SIMPLE_NAME; 055 } 056 057 /** gets the annotation but also adds an import in the process if a Convert annotation is required. */ 058 @Override 059 protected NodeData getAnnotationNodes(String enclosingClass, String fieldName, String mappedClass) { 060 final CollectionDescriptor cld = OjbUtil.findCollectionDescriptor(mappedClass, fieldName, descriptorRepositories); 061 if (cld != null) { 062 final List<MemberValuePair> pairs = new ArrayList<MemberValuePair>(); 063 final Collection<ImportDeclaration> additionalImports = new ArrayList<ImportDeclaration>(); 064 065 if (cld.isMtoNRelation()) { 066 return null; 067 } 068 /* I don't think this is correct. 069 final String[] fkToItemClass = getFksToItemClass(cld); 070 if (fkToItemClass != null || fkToItemClass.length != 0) { 071 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has a collection descriptor for " + fieldName 072 + " for a 1:M relationship but has fk-pointing-to-element-class configured"); 073 } 074 075 final String[] fkToThisClass = getFksToThisClass(cld); 076 if (fkToThisClass != null || fkToThisClass.length != 0) { 077 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has a collection descriptor for " + fieldName 078 + " for a 1:M relationship but has fk-pointing-to-this-class configured"); 079 } 080 */ 081 final Collection<String> fks = cld.getForeignKeyFields(); 082 if (fks == null || fks.isEmpty()) { 083 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has a collection descriptor for " + fieldName 084 + " but does not have any foreign keys configured"); 085 return null; 086 } 087 088 final String itemClassName = cld.getItemClassName(); 089 if (StringUtils.isBlank(itemClassName)) { 090 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has a reference descriptor for " + fieldName 091 + " but does not class name attribute"); 092 } else { 093 final String shortClassName = ClassUtils.getShortClassName(itemClassName); 094 final String packageName = ClassUtils.getPackageName(itemClassName); 095 pairs.add(new MemberValuePair("targetEntity", new NameExpr(shortClassName + ".class"))); 096 additionalImports.add(new ImportDeclaration(new QualifiedNameExpr(new NameExpr(packageName), shortClassName), false, false)); 097 } 098 099 final boolean proxy = cld.isLazy(); 100 if (proxy) { 101 pairs.add(new MemberValuePair("fetch", new NameExpr("FetchType.LAZY"))); 102 additionalImports.add(new ImportDeclaration(new QualifiedNameExpr(new NameExpr(PACKAGE), "FetchType"), false, false)); 103 } 104 105 final boolean refresh = cld.isRefresh(); 106 if (refresh) { 107 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has refresh set to " + refresh + ", unsupported conversion to @OneToOne attributes"); 108 } 109 110 final List<Expression> cascadeTypes = new ArrayList<Expression>(); 111 final boolean autoRetrieve = cld.getCascadeRetrieve(); 112 if (autoRetrieve) { 113 cascadeTypes.add(new NameExpr("CascadeType.REFRESH")); 114 } else { 115 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-retrieve set to " + autoRetrieve + ", unsupported conversion to CascadeType"); 116 } 117 118 final int autoDelete = cld.getCascadingDelete(); 119 if (autoDelete == ObjectReferenceDescriptor.CASCADE_NONE) { 120 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-delete set to none, unsupported conversion to CascadeType"); 121 } else if (autoDelete == ObjectReferenceDescriptor.CASCADE_LINK) { 122 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-delete set to link, unsupported conversion to CascadeType"); 123 } else if (autoDelete == ObjectReferenceDescriptor.CASCADE_OBJECT) { 124 cascadeTypes.add(new NameExpr("CascadeType.REMOVE")); 125 pairs.add(new MemberValuePair("orphanRemoval", new BooleanLiteralExpr(true))); 126 } else { 127 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-delete set to an invalid value"); 128 } 129 130 final int autoUpdate = cld.getCascadingStore(); 131 if (autoUpdate == ObjectReferenceDescriptor.CASCADE_NONE) { 132 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-update set to none, unsupported conversion to CascadeType"); 133 } else if (autoUpdate == ObjectReferenceDescriptor.CASCADE_LINK) { 134 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-update set to link, unsupported conversion to CascadeType"); 135 } else if (autoUpdate == ObjectReferenceDescriptor.CASCADE_OBJECT) { 136 cascadeTypes.add(new NameExpr("CascadeType.PERSIST")); 137 } else { 138 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-update set to an invalid value"); 139 } 140 141 if (!cascadeTypes.isEmpty()) { 142 pairs.add(new MemberValuePair("cascade", new ArrayInitializerExpr(cascadeTypes))); 143 additionalImports.add(new ImportDeclaration(new QualifiedNameExpr(new NameExpr(PACKAGE), "CascadeType"), false, false)); 144 } 145 146 final NodeData nodeData; 147 if (isBidirectional(mappedClass, itemClassName)) { 148 LOG.info(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " bi-directional OneToMany relationship detected"); 149 150 BidirectionalOwnerRegistry registry = BidirectionalOwnerRegistry.getInstance(); 151 if (registry.isOwnerItemClassManyToOne(mappedClass, itemClassName)) { 152 nodeData = new NodeData(new NormalAnnotationExpr(new NameExpr(SIMPLE_NAME), 153 Collections.singletonList(new MemberValuePair("mappedBy", new StringLiteralExpr(getMappedBy( 154 mappedClass, itemClassName))))), 155 new ImportDeclaration(new QualifiedNameExpr(new NameExpr(PACKAGE), SIMPLE_NAME), false, false), 156 additionalImports); 157 } else { 158 registry.assignItemClassAsOwnerManyToOne(mappedClass, itemClassName); 159 160 nodeData = new NodeData(new NormalAnnotationExpr(new NameExpr(SIMPLE_NAME), 161 Collections.singletonList(new MemberValuePair("mappedBy", new StringLiteralExpr(getMappedBy( 162 mappedClass, itemClassName))))), 163 new ImportDeclaration(new QualifiedNameExpr(new NameExpr(PACKAGE), SIMPLE_NAME), false, false), 164 additionalImports); 165 } 166 } else { 167 nodeData = new NodeData(new NormalAnnotationExpr(new NameExpr(SIMPLE_NAME), pairs), 168 new ImportDeclaration(new QualifiedNameExpr(new NameExpr(PACKAGE), SIMPLE_NAME), false, false), 169 additionalImports); 170 } 171 172 return nodeData; 173 } 174 return null; 175 } 176 177 private String[] getFksToItemClass(CollectionDescriptor cld) { 178 try { 179 return cld.getFksToItemClass(); 180 } catch (NullPointerException e) { 181 return new String[] {}; 182 } 183 } 184 185 private String [] getFksToThisClass(CollectionDescriptor cld) { 186 try { 187 return cld.getFksToItemClass(); 188 } catch (NullPointerException e) { 189 return new String[] {}; 190 } 191 } 192 193 private boolean isBidirectional(String thisClass, String itemClass) { 194 final ClassDescriptor cd = OjbUtil.findClassDescriptor(itemClass, descriptorRepositories); 195 Collection<ObjectReferenceDescriptor> ords = cd.getObjectReferenceDescriptors(); 196 if (ords != null) { 197 for (ObjectReferenceDescriptor ord : ords) { 198 if (ord.getItemClassName().equals(thisClass)) { 199 return true; 200 } 201 } 202 } 203 return false; 204 } 205 206 private String getMappedBy(String thisClass, String itemClass) { 207 final ClassDescriptor cd = OjbUtil.findClassDescriptor(itemClass, descriptorRepositories); 208 Collection<ObjectReferenceDescriptor> ords = cd.getObjectReferenceDescriptors(); 209 if (ords != null) { 210 for (ObjectReferenceDescriptor ord : ords) { 211 if (ord.getItemClassName().equals(thisClass)) { 212 return ord.getAttributeName(); 213 } 214 } 215 } 216 return null; 217 } 218}