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.Expression; 021import japa.parser.ast.expr.MemberValuePair; 022import japa.parser.ast.expr.NameExpr; 023import japa.parser.ast.expr.NormalAnnotationExpr; 024import japa.parser.ast.expr.QualifiedNameExpr; 025import japa.parser.ast.expr.StringLiteralExpr; 026import org.apache.commons.lang.StringUtils; 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.apache.ojb.broker.metadata.CollectionDescriptor; 030import org.apache.ojb.broker.metadata.DescriptorRepository; 031import org.kuali.rice.devtools.jpa.eclipselink.conv.ojb.OjbUtil; 032import org.kuali.rice.devtools.jpa.eclipselink.conv.parser.helper.NodeData; 033 034import java.util.ArrayList; 035import java.util.Collection; 036import java.util.List; 037 038/** 039 * Join Table annotations can be used by many associations besides M:N however we are only using it for M:N. 040 */ 041public class JoinTableResolver extends AbstractMappedFieldResolver { 042 private static final Log LOG = LogFactory.getLog(JoinTableResolver.class); 043 044 public static final String PACKAGE = "javax.persistence"; 045 public static final String SIMPLE_NAME = "JoinTable"; 046 047 public JoinTableResolver(Collection<DescriptorRepository> descriptorRepositories) { 048 super(descriptorRepositories); 049 } 050 @Override 051 public String getFullyQualifiedName() { 052 return PACKAGE + "." + SIMPLE_NAME; 053 } 054 055 /** gets the annotation but also adds an import in the process if a Convert annotation is required. */ 056 @Override 057 protected NodeData getAnnotationNodes(String enclosingClass, String fieldName, String mappedClass) { 058 final CollectionDescriptor cld = OjbUtil.findCollectionDescriptor(mappedClass, fieldName, descriptorRepositories); 059 if (cld != null) { 060 final List<MemberValuePair> pairs = new ArrayList<MemberValuePair>(); 061 final Collection<ImportDeclaration> additionalImports = new ArrayList<ImportDeclaration>(); 062 063 if (!cld.isMtoNRelation()) { 064 return null; 065 } 066 067 boolean error = false; 068 final String joinTable = cld.getIndirectionTable(); 069 if (StringUtils.isBlank(joinTable)) { 070 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has a collection descriptor for " + fieldName 071 + " for a M:N relationship but does not have an indirection table configured"); 072 error = true; 073 } else { 074 pairs.add(new MemberValuePair("name", new StringLiteralExpr(joinTable))); 075 } 076 077 final String[] fkToItemClass = getFksToItemClass(cld); 078 if (fkToItemClass == null || fkToItemClass.length == 0) { 079 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has a collection descriptor for " + fieldName 080 + " for a M:N relationship but does not have any fk-pointing-to-element-class configured"); 081 error = true; 082 } 083 084 final String itemClassName = cld.getItemClassName(); 085 if (StringUtils.isBlank(itemClassName)) { 086 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has a reference descriptor for " + fieldName 087 + " but does not class name attribute"); 088 error = true; 089 } 090 091 if (error) { 092 return null; 093 } 094 095 final List<Expression> joinColumns = new ArrayList<Expression>(); 096 for (String fk : fkToItemClass) { 097 final List<MemberValuePair> joinColumnsPairs = new ArrayList<MemberValuePair>(); 098 joinColumnsPairs.add(new MemberValuePair("name", new StringLiteralExpr(fk))); 099 final Collection<String> pks = OjbUtil.getPrimaryKeyNames(itemClassName, descriptorRepositories); 100 joinColumnsPairs.add(new MemberValuePair("referencedColumnName", new StringLiteralExpr(getPksAsString(pks)))); 101 joinColumns.add(new NormalAnnotationExpr(new NameExpr("JoinColumn"), joinColumnsPairs)); 102 } 103 pairs.add(new MemberValuePair("joinColumns", new ArrayInitializerExpr(joinColumns))); 104 additionalImports.add(new ImportDeclaration(new QualifiedNameExpr(new NameExpr(PACKAGE), "JoinColumn"), false, false)); 105 106 final String[] fkToThisClass = getFksToThisClass(cld); 107 if (fkToThisClass == null || fkToThisClass.length == 0) { 108 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has a collection descriptor for " + fieldName 109 + " for a M:N relationship but does not have any fk-pointing-to-this-class configured"); 110 return null; 111 } else { 112 final List<Expression> invJoinColumns = new ArrayList<Expression>(); 113 for (String fk : fkToItemClass) { 114 final List<MemberValuePair> invJoinColumnsPairs = new ArrayList<MemberValuePair>(); 115 invJoinColumnsPairs.add(new MemberValuePair("name", new StringLiteralExpr(fk))); 116 final Collection<String> pks = OjbUtil.getPrimaryKeyNames(mappedClass, descriptorRepositories); 117 invJoinColumnsPairs.add(new MemberValuePair("referencedColumnName", new StringLiteralExpr( 118 getPksAsString(pks)))); 119 invJoinColumns.add(new NormalAnnotationExpr(new NameExpr("JoinColumn"), invJoinColumnsPairs)); 120 } 121 pairs.add(new MemberValuePair("inverseJoinColumns", new ArrayInitializerExpr(invJoinColumns))); 122 } 123 124 125 final Collection<String> fks = cld.getForeignKeyFields(); 126 if (fks != null || !fks.isEmpty()) { 127 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has a collection descriptor for " + fieldName 128 + " for a M:N relationship but has the inverse-foreignkey configured as opposed to " 129 + "fk-pointing-to-this-class and fk-pointing-to-element-class"); 130 } 131 132 return new NodeData(new NormalAnnotationExpr(new NameExpr(SIMPLE_NAME), pairs), 133 new ImportDeclaration(new QualifiedNameExpr(new NameExpr(PACKAGE), SIMPLE_NAME), false, false), 134 additionalImports); 135 } 136 return null; 137 } 138 139 private String getPksAsString(Collection<String> descriptors) { 140 if (descriptors.size() == 1) { 141 return descriptors.iterator().next(); 142 } 143 String pks = ""; 144 for (String d : descriptors) { 145 pks += d + "|"; 146 } 147 return pks; 148 } 149 150 private String[] getFksToItemClass(CollectionDescriptor cld) { 151 try { 152 return cld.getFksToItemClass(); 153 } catch (NullPointerException e) { 154 return new String[] {}; 155 } 156 } 157 158 private String[] getFksToThisClass(CollectionDescriptor cld) { 159 try { 160 return cld.getFksToItemClass(); 161 } catch (NullPointerException e) { 162 return new String[] {}; 163 } 164 } 165}