001/** 002 * Copyright 2005-2015 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.comments.BlockComment; 019import japa.parser.ast.comments.Comment; 020import japa.parser.ast.ImportDeclaration; 021import japa.parser.ast.expr.ArrayInitializerExpr; 022import japa.parser.ast.expr.BooleanLiteralExpr; 023import japa.parser.ast.expr.Expression; 024import japa.parser.ast.expr.MemberValuePair; 025import japa.parser.ast.expr.NameExpr; 026import japa.parser.ast.expr.NormalAnnotationExpr; 027import japa.parser.ast.expr.QualifiedNameExpr; 028import org.apache.commons.lang.ClassUtils; 029import org.apache.commons.lang.StringUtils; 030import org.apache.logging.log4j.Logger; 031import org.apache.logging.log4j.LogManager; 032import org.apache.ojb.broker.metadata.ClassDescriptor; 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.List; 041 042public class OneToOneResolver extends AbstractMappedFieldResolver { 043 private static final Logger LOG = LogManager.getLogger(OneToOneResolver.class); 044 045 public static final String PACKAGE = "javax.persistence"; 046 public static final String SIMPLE_NAME = "OneToOne"; 047 048 public OneToOneResolver(Collection<DescriptorRepository> descriptorRepositories) { 049 super(descriptorRepositories); 050 } 051 @Override 052 public String getFullyQualifiedName() { 053 return PACKAGE + "." + SIMPLE_NAME; 054 } 055 056 /** gets the annotation but also adds an import in the process if a Convert annotation is required. */ 057 @Override 058 protected NodeData getAnnotationNodes(String enclosingClass, String fieldName, String mappedClass) { 059 final ObjectReferenceDescriptor ord = OjbUtil.findObjectReferenceDescriptor(mappedClass, fieldName, descriptorRepositories); 060 if (ord != null) { 061 final List<MemberValuePair> pairs = new ArrayList<MemberValuePair>(); 062 final Collection<ImportDeclaration> additionalImports = new ArrayList<ImportDeclaration>(); 063 064 final Collection<String> fks = ord.getForeignKeyFields(); 065 if (fks == null || fks.isEmpty()) { 066 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has a reference descriptor for " + fieldName 067 + " but does not have any foreign keys configured"); 068 return null; 069 } 070 071 final Collection<String> pks = OjbUtil.getPrimaryKeyNames(mappedClass, descriptorRepositories); 072 073 if (pks.size() == fks.size() && pks.containsAll(fks)) { 074 final String itemClassName = ord.getItemClassName(); 075 if (StringUtils.isBlank(itemClassName)) { 076 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has a reference descriptor for " + fieldName 077 + " but does not class name attribute"); 078 } else { 079 final String shortClassName = ClassUtils.getShortClassName(itemClassName); 080 final String packageName = ClassUtils.getPackageName(itemClassName); 081 pairs.add(new MemberValuePair("targetEntity", new NameExpr(shortClassName + ".class"))); 082 additionalImports.add(new ImportDeclaration(new QualifiedNameExpr(new NameExpr(packageName), shortClassName), false, false)); 083 } 084 085 final boolean proxy = ord.isLazy(); 086 if (proxy) { 087 pairs.add(new MemberValuePair("fetch", new NameExpr("FetchType.LAZY"))); 088 additionalImports.add(new ImportDeclaration(new QualifiedNameExpr(new NameExpr(PACKAGE), "FetchType"), false, false)); 089 } 090 091 final boolean refresh = ord.isRefresh(); 092 if (refresh) { 093 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has refresh set to " + refresh + ", unsupported conversion to @OneToOne attributes"); 094 } 095 096 final List<Expression> cascadeTypes = new ArrayList<Expression>(); 097 final boolean autoRetrieve = ord.getCascadeRetrieve(); 098 if (autoRetrieve) { 099 cascadeTypes.add(new NameExpr("CascadeType.REFRESH")); 100 } else { 101 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-retrieve set to " + autoRetrieve + ", unsupported conversion to CascadeType"); 102 } 103 104 final int autoDelete = ord.getCascadingDelete(); 105 if (autoDelete == ObjectReferenceDescriptor.CASCADE_NONE) { 106 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-delete set to none, unsupported conversion to CascadeType"); 107 } else if (autoDelete == ObjectReferenceDescriptor.CASCADE_LINK) { 108 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-delete set to link, unsupported conversion to CascadeType"); 109 } else if (autoDelete == ObjectReferenceDescriptor.CASCADE_OBJECT) { 110 cascadeTypes.add(new NameExpr("CascadeType.REMOVE")); 111 pairs.add(new MemberValuePair("orphanRemoval", new BooleanLiteralExpr(true))); 112 } else { 113 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-delete set to an invalid value"); 114 } 115 116 final int autoUpdate = ord.getCascadingStore(); 117 if (autoUpdate == ObjectReferenceDescriptor.CASCADE_NONE) { 118 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-update set to none, unsupported conversion to CascadeType"); 119 } else if (autoUpdate == ObjectReferenceDescriptor.CASCADE_LINK) { 120 LOG.warn(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-update set to link, unsupported conversion to CascadeType"); 121 } else if (autoUpdate == ObjectReferenceDescriptor.CASCADE_OBJECT) { 122 cascadeTypes.add(new NameExpr("CascadeType.PERSIST")); 123 } else { 124 LOG.error(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " field has auto-update set to an invalid value"); 125 } 126 127 if (!cascadeTypes.isEmpty()) { 128 pairs.add(new MemberValuePair("cascade", new ArrayInitializerExpr(cascadeTypes))); 129 additionalImports.add(new ImportDeclaration(new QualifiedNameExpr(new NameExpr(PACKAGE), "CascadeType"), false, false)); 130 } 131 final NodeData nodeData; 132 if (isBidirectional(mappedClass, itemClassName)) { 133 LOG.info(ResolverUtil.logMsgForField(enclosingClass, fieldName, mappedClass) + " bi-directional OneToOne relationship detected"); 134 135 final String mappedBy = getMappedBy(mappedClass, itemClassName); 136 final Comment fixme = new BlockComment("\nFIXME: JPA_CONVERSION \n" 137 + "For one-to-one bidirectional relationships, the owning side corresponds to the side that contains the corresponding foreign key.\n" 138 + "Even in the absence of a foreign key, one side must be the owning side.\n" 139 + "If this is not the owning side, the required annotation should be:\n@OneToOne(mappedBy=\"" + mappedBy + "\")\n"); 140 final NormalAnnotationExpr annotation = new NormalAnnotationExpr(new NameExpr(SIMPLE_NAME), pairs); 141 annotation.setComment(fixme); 142 nodeData = new NodeData(annotation, 143 new ImportDeclaration(new QualifiedNameExpr(new NameExpr(PACKAGE), SIMPLE_NAME), false, false), 144 additionalImports); 145 146 } else { 147 nodeData = new NodeData(new NormalAnnotationExpr(new NameExpr(SIMPLE_NAME), pairs), 148 new ImportDeclaration(new QualifiedNameExpr(new NameExpr(PACKAGE), SIMPLE_NAME), false, false), 149 additionalImports); 150 } 151 return nodeData; 152 } 153 } 154 return null; 155 } 156 157 private boolean isBidirectional(String thisClass, String itemClass) { 158 final ClassDescriptor cd = OjbUtil.findClassDescriptor(itemClass, descriptorRepositories); 159 if (cd != null) { 160 Collection<ObjectReferenceDescriptor> ords = cd.getObjectReferenceDescriptors(); 161 if (ords != null) { 162 for (ObjectReferenceDescriptor ord : ords) { 163 if (ord.getItemClassName().equals(thisClass)) { 164 return true; 165 } 166 } 167 } 168 } 169 return false; 170 } 171 172 private String getMappedBy(String thisClass, String itemClass) { 173 final ClassDescriptor cd = OjbUtil.findClassDescriptor(itemClass, descriptorRepositories); 174 if (cd != null) { 175 Collection<ObjectReferenceDescriptor> ords = cd.getObjectReferenceDescriptors(); 176 if (ords != null) { 177 for (ObjectReferenceDescriptor ord : ords) { 178 if (ord.getItemClassName().equals(thisClass)) { 179 return ord.getAttributeName(); 180 } 181 } 182 } 183 } 184 return null; 185 } 186}