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    }