001 /* 002 Copyright (C) 2001-2004 Laurent Martelli <laurent@aopsys.com> 003 004 This program is free software; you can redistribute it and/or modify 005 it under the terms of the GNU Lesser General Public License as 006 published by the Free Software Foundation; either version 2 of the 007 License, or (at your option) any later version. 008 009 This program is distributed in the hope that it will be useful, 010 but WITHOUT ANY WARRANTY; without even the implied warranty of 011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 GNU Lesser General Public License for more details. 013 014 You should have received a copy of the GNU Lesser General Public License 015 along with this program; if not, write to the Free Software 016 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 017 018 package org.objectweb.jac.aspects.integrity; 019 020 import org.aopalliance.intercept.ConstructorInvocation; 021 import org.aopalliance.intercept.MethodInvocation; 022 import org.apache.log4j.Logger; 023 import org.objectweb.jac.core.AspectComponent; 024 import org.objectweb.jac.core.Collaboration; 025 import org.objectweb.jac.core.Interaction; 026 import org.objectweb.jac.core.Wrapper; 027 import org.objectweb.jac.core.rtti.ClassItem; 028 import org.objectweb.jac.core.rtti.CollectionItem; 029 import org.objectweb.jac.core.rtti.FieldItem; 030 import org.objectweb.jac.core.rtti.MethodItem; 031 import org.objectweb.jac.core.rtti.RttiAC; 032 033 /** 034 * Wrapper for updating opposite roles. 035 */ 036 037 public class RoleWrapper extends Wrapper { 038 static final Logger logger = Logger.getLogger("integrity"); 039 040 public RoleWrapper(AspectComponent ac) { 041 super(ac); 042 } 043 044 /** 045 * Tells the integrity aspect to update the opposite roles of the 046 * declared associations. 047 */ 048 public Object updateOppositeRole(Interaction interaction) { 049 logger.debug("updateOppositeRoles " + interaction.method + "..."); 050 if (attr("rtti.updateOpposite") != null || 051 attr(IntegrityAC.DISABLE_ROLE_UPDATES)!=null) 052 return interaction.proceed(); 053 054 doUpdate(interaction); 055 056 Object result = interaction.proceed(); 057 return result; 058 } 059 060 protected void doUpdate(Interaction interaction) { 061 logger.debug( 062 "updateOppositeRoles "+interaction.wrappee+"."+ 063 interaction.method+"("+interaction.args[0]+")"); 064 ClassItem cli = interaction.getClassItem(); 065 MethodItem method = (MethodItem) interaction.method; 066 067 // References 068 FieldItem[] fields = method.getWrittenFields(); 069 FieldItem opposite; 070 Object newValue = interaction.args[0]; 071 for (int i = 0; fields != null && i < fields.length; i++) { 072 FieldItem field = fields[i]; 073 Object currentValue = field.getThroughAccessor(interaction.wrappee); 074 opposite = (FieldItem) field.getAttribute(RttiAC.OPPOSITE_ROLE); 075 if (opposite != null) { 076 try { 077 attrdef("rtti.updateOpposite", opposite); 078 logger.debug( 079 "UpdateOppositeRole in "+interaction.method+" : "+ 080 fields[i].getName()+" -> "+opposite.getName()); 081 logger.debug( 082 "wrappee = "+interaction.wrappee+" ; "+ 083 "currentValue = "+currentValue); 084 if (opposite instanceof CollectionItem) { 085 CollectionItem oppositeCollection = 086 (CollectionItem) opposite; 087 if (currentValue != null) 088 oppositeCollection.removeThroughRemover( 089 currentValue, 090 interaction.wrappee); 091 if (newValue != null 092 && !oppositeCollection.contains( 093 newValue, 094 interaction.wrappee)) 095 oppositeCollection.addThroughAdder( 096 newValue, 097 interaction.wrappee); 098 } else { 099 if (currentValue != null) { 100 opposite.setThroughWriter(currentValue, null); 101 } 102 if (newValue!=null) 103 opposite.setThroughWriter( 104 newValue, 105 interaction.wrappee); 106 } 107 } catch (Exception e) { 108 logger.error( 109 "Failed to update opposite role "+opposite.getLongName()+" of "+ 110 field.getLongName()+" on "+interaction.wrappee,e); 111 } finally { 112 attrdef("rtti.updateOpposite", null); 113 } 114 } 115 } 116 117 // Adders 118 CollectionItem[] collections = method.getAddedCollections(); 119 Object addedValue = interaction.args[0]; 120 for (int i = 0; collections != null && i < collections.length; i++) { 121 CollectionItem collection = collections[i]; 122 opposite = 123 (FieldItem) collection.getAttribute(RttiAC.OPPOSITE_ROLE); 124 if (opposite == null) { 125 logger.debug( 126 collection.getParent().getName()+"."+collection.toString()+ 127 " has no opposite role"); 128 continue; 129 } 130 logger.debug( 131 "UpdateOppositeRole #"+i+" in "+interaction.method+" : "+ 132 collection.getName()+" -> "+opposite.getName()); 133 try { 134 attrdef("rtti.updateOpposite", opposite); 135 if (opposite instanceof CollectionItem) { 136 CollectionItem oppositeCollection = 137 (CollectionItem) opposite; 138 if (addedValue != null 139 && !oppositeCollection.contains( 140 addedValue, 141 interaction.wrappee)) 142 oppositeCollection.addThroughAdder( 143 addedValue, 144 interaction.wrappee); 145 } else { 146 if (addedValue != null) { 147 Object currentValue = opposite.get(addedValue); 148 logger.debug(" currentValue="+currentValue+ 149 ", interaction.wrappee="+interaction.wrappee); 150 if (currentValue != null && 151 currentValue != interaction.wrappee) { 152 collection.removeThroughRemover( 153 currentValue, 154 addedValue); 155 } 156 opposite.setThroughWriter( 157 addedValue, 158 interaction.wrappee); 159 } 160 } 161 } catch (Exception e) { 162 logger.error( 163 "Failed to update opposite role "+opposite.getLongName()+" of "+ 164 collection.getLongName()+" on "+interaction.wrappee,e); 165 } finally { 166 attrdef("rtti.updateOpposite", null); 167 } 168 } 169 170 // Removers 171 collections = method.getRemovedCollections(); 172 Object removedValue = interaction.args[0]; 173 for (int i = 0; collections != null && i < collections.length; i++) { 174 CollectionItem collection = collections[i]; 175 logger.debug("removed collection " + collection); 176 opposite = 177 (FieldItem) collection.getAttribute(RttiAC.OPPOSITE_ROLE); 178 try { 179 attrdef("rtti.updateOpposite", opposite); 180 if (opposite == null) { 181 logger.debug( 182 collection.getParent().getName()+"."+collection.toString()+ 183 " has no opposite role"); 184 continue; 185 } 186 logger.debug( 187 "UpdateOppositeRole in "+interaction.method+" : "+collection.getName()+ 188 " -> "+opposite.getName()); 189 if (removedValue != null) { 190 if (opposite instanceof CollectionItem) { 191 ((CollectionItem) opposite).removeThroughRemover( 192 removedValue, 193 interaction.wrappee); 194 } else { 195 opposite.setThroughWriter(removedValue, null); 196 } 197 } 198 } catch (Exception e) { 199 logger.error("Failed to update opposite role "+opposite.getLongName()+" of " 200 +collection.getLongName()+" on "+interaction.wrappee,e); 201 } finally { 202 attrdef("rtti.updateOpposite", null); 203 } 204 205 } 206 } 207 208 /** 209 * Initializes the opposite role of an object auto created on an 210 * adder or reference setter if there's an attribute 211 * GuiAC.AUTOCREATE_REASON in the context, whose value is an 212 * Interaction. */ 213 public Object initAutoCreatedObject(Interaction interaction) { 214 Object result = interaction.proceed(); 215 Collaboration collab = Collaboration.get(); 216 Interaction reason = 217 (Interaction) collab.getAttribute("GuiAC.AUTOCREATE_REASON"); 218 if (reason != null) { 219 logger.debug( 220 "initAutoCreatedObject for " 221 + interaction + " because of " + reason); 222 reason.args[0] = interaction.wrappee; 223 // We better unset the attribute now because doUpdate() may 224 // create objects (under weird circumtances) and we do not 225 // want to apply the initialization on them 226 collab.removeAttribute("GuiAC.AUTOCREATE_REASON"); 227 doUpdate(reason); 228 } 229 return result; 230 } 231 232 /** 233 * Disable opposite role updating 234 * @param role role for which opposite role updating must be disabled 235 */ 236 public static void disableRoleUpdate(FieldItem role) { 237 Collaboration.get().addAttribute("rtti.updateOpposite", role); 238 } 239 240 public static void enableRoleUpdate(FieldItem role) { 241 Collaboration.get().removeAttribute("rtti.updateOpposite"); 242 } 243 244 public Object invoke(MethodInvocation invocation) throws Throwable { 245 return this.updateOppositeRole((Interaction) invocation); 246 } 247 248 public Object construct(ConstructorInvocation invocation) 249 throws Throwable 250 { 251 return this.initAutoCreatedObject((Interaction) invocation); 252 } 253 }