001    package org.apache.ojb.broker.metadata;
002    
003    /* Copyright 2003-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.util.List;
019    import java.util.Map;
020    import java.util.HashMap;
021    
022    import org.apache.commons.lang.SystemUtils;
023    import org.apache.ojb.broker.metadata.fieldaccess.AnonymousPersistentField;
024    import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
025    import org.apache.ojb.broker.metadata.fieldaccess.PersistentFieldFactory;
026    import org.apache.ojb.broker.util.ClassHelper;
027    import org.apache.ojb.broker.util.logging.Logger;
028    import org.apache.ojb.broker.util.logging.LoggerFactory;
029    
030    /**
031     * This class handle inheritance as 1-1 association based on a anonymous field
032     * (no field in persistent object needed).
033     *
034     * @version $Id: SuperReferenceDescriptor.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
035     */
036    public class SuperReferenceDescriptor extends ObjectReferenceDescriptor
037    {
038        private transient Logger log;
039    
040        public static final String SUPER_FIELD_INTERNAL_NAME = "ojbSuperFieldInternal";
041        public static final String SUPER_FIELD_NAME = RepositoryElements.TAG_SUPER;
042    
043        private Boolean javaInheritance;
044        private Map declaredInheritanceFields = new HashMap();
045    
046        public SuperReferenceDescriptor(ClassDescriptor descriptor)
047        {
048            super(descriptor);
049            // most important call, create new specific field for inheritance
050            super.setPersistentField(new SuperReferenceField(this));
051            // needed immutable settings
052            super.setLazy(false);
053            super.setCascadeRetrieve(true);
054            super.setCascadingStore(CASCADE_OBJECT);
055            super.setCascadingDelete(CASCADE_OBJECT);
056        }
057    
058        public boolean isSuperReferenceDescriptor()
059        {
060            return true;
061        }
062    
063        public void setItemClass(Class c)
064        {
065            super.setItemClass(c);
066            getClassDescriptor().setBaseClass(c.getName());
067        }
068    
069        /**
070         * Noop, a specific {@link org.apache.ojb.broker.metadata.fieldaccess.PersistentField} is
071         * used internal - {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor.SuperReferenceField}.
072         */
073        public void setPersistentField(Class c, String fieldname)
074        {
075            // noop
076        }
077    
078        /**
079         * Noop, a specific {@link org.apache.ojb.broker.metadata.fieldaccess.PersistentField} is
080         * used internal - {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor.SuperReferenceField}.
081         */
082        public void setPersistentField(PersistentField pf)
083        {
084            // noop
085        }
086    
087        public void setLazy(boolean lazy)
088        {
089            getLog().info("Not allowed to change this property, will ignore setting");
090        }
091    
092        public void setCascadeRetrieve(boolean b)
093        {
094            getLog().info("Not allowed to change this property, will ignore setting");
095        }
096    
097        public void setCascadingStore(int cascade)
098        {
099            getLog().info("Not allowed to change this property, will ignore setting");
100        }
101    
102        public void setCascadingStore(String value)
103        {
104            getLog().info("Not allowed to change this property, will ignore setting");
105        }
106    
107        public void setCascadingDelete(int cascade)
108        {
109            getLog().info("Not allowed to change this property, will ignore setting");
110        }
111    
112        public void setCascadingDelete(String value)
113        {
114            getLog().info("Not allowed to change this property, will ignore setting");
115        }
116    
117        public void setCascadeStore(boolean cascade)
118        {
119            getLog().info("Not allowed to change this property, will ignore setting");
120        }
121    
122        public void setCascadeDelete(boolean cascade)
123        {
124            getLog().info("Not allowed to change this property, will ignore setting");
125        }
126    
127        public SuperReferenceField getInheritanceField()
128        {
129            return (SuperReferenceField) getPersistentField();
130        }
131    
132        /**
133         * If this method returns <em>true</em> the inheritance described by this object
134         * is a <em>normal</em> JAVA inheritance. If <em>false</em> the inheritance is only declared
135         * in the O/R mapping it's a <em>declarative inheritance</em>, the referenced "super class" in <strong>not</strong>
136         * a JAVA super class of the main class.
137         */
138        public boolean isJavaInheritance()
139        {
140            if(javaInheritance == null)
141            {
142                javaInheritance = getClassDescriptor().getSuperClassDescriptor().getClassOfObject()
143                        .isAssignableFrom(getClassDescriptor().getClassOfObject()) ? Boolean.TRUE : Boolean.FALSE;
144            }
145            return javaInheritance.booleanValue();
146        }
147    
148        synchronized PersistentField getDeclaredInheritanceField(Class target, String name)
149        {
150            Map fields = (HashMap) declaredInheritanceFields.get(target);
151            if(fields == null)
152            {
153                fields = new HashMap();
154                declaredInheritanceFields.put(target, fields);
155            }
156            PersistentField pf = (PersistentField) fields.get(name);
157            if(pf == null)
158            {
159                pf = PersistentFieldFactory.createPersistentField(target, name);
160                // System.out.println("## tmp field: " + target + ", name: " + name + ", field: " + pf);
161                fields.put(name, pf);
162            }
163            return pf;
164        }
165    
166        private Logger getLog()
167        {
168            if(log == null)
169            {
170                log = LoggerFactory.getLogger(SuperReferenceField.class);
171            }
172            return log;
173        }
174    
175    
176        //====================================================
177        // inner class
178        //====================================================
179    
180        public static final class SuperReferenceField extends AnonymousPersistentField
181        {
182            private transient Logger log;
183    
184            private SuperReferenceDescriptor superRef;
185    
186            public SuperReferenceField(SuperReferenceDescriptor superRef)
187            {
188                super(SUPER_FIELD_INTERNAL_NAME);
189                this.superRef = superRef;
190            }
191    
192            private Logger getLog()
193            {
194                if(log == null)
195                {
196                    log = LoggerFactory.getLogger(SuperReferenceField.class);
197                }
198                return log;
199            }
200    
201            /**
202             * Field values of 'value' (base object) are copied to 'obj' (derived object)
203             * then obj is saved in a map
204             *
205             * @param target - the base object instance
206             * @param value  - the derived object instance
207             * @throws MetadataException
208             */
209            public synchronized void set(Object target, Object value) throws MetadataException
210            {
211                // System.out.println("target: " + target + " value: " + value);
212                ClassDescriptor superCld = superRef.getClassDescriptor().getSuperClassDescriptor();
213                if(superRef.isJavaInheritance())
214                {
215                    copyFields(superCld, target, superCld, value, true, true);
216                }
217                else
218                {
219                    copyFields(superRef.getClassDescriptor(), target, superCld, value, false, false);
220                }
221            }
222    
223            /**
224             * Field values of specified 'obj' (the derived object) are copied to
225             * 'value' (base object) then value is returned as a referenced object.
226             * If the base object is the super class of the specified 'obj', then
227             * return the specified object.
228             * Else a base class instance will be created at runtime and the field values
229             * from the derived object are copied to the base class object.
230             *
231             * @param obj - the base object instance
232             * @throws MetadataException
233             */
234            public synchronized Object get(Object obj) throws MetadataException
235            {
236                if(obj == null) return null;
237                if(superRef.isJavaInheritance())
238                {
239                    return obj;
240                }
241                else
242                {
243                    return getObjectWithDeclaredSuperClass(obj);
244                }
245            }
246    
247            private Object getObjectWithDeclaredSuperClass(Object obj)
248            {
249                Object value = getFromFieldCache(obj);
250                if(value == null)
251                {
252                    ClassDescriptor baseCld = null;
253                    try
254                    {
255                        baseCld = superRef.getClassDescriptor().getSuperClassDescriptor();
256                        value = ClassHelper.buildNewObjectInstance(baseCld);
257                    }
258                    catch(Exception e)
259                    {
260                        throw new MetadataException("Can't create new base class object for '"
261                                + (baseCld != null ? baseCld.getClassNameOfObject() : null) + "'", e);
262                    }
263                    copyFields(baseCld, value, superRef.getClassDescriptor(), obj, true, false);
264                    putToFieldCache(obj, value);
265                }
266                return value;
267            }
268    
269            void copyFields(ClassDescriptor targetCld, Object target, ClassDescriptor sourceCld, Object source, boolean targetIsSuper, boolean javaInheritance)
270            {
271                if(getLog().isDebugEnabled())
272                {
273                    String msg = ("Copy fields from " + SystemUtils.LINE_SEPARATOR
274                            + "source object '" + (source != null ? source.getClass().getName() : null) + "'" + SystemUtils.LINE_SEPARATOR
275                            + "using source fields declared in '" + sourceCld.getClassNameOfObject() + "'" + SystemUtils.LINE_SEPARATOR
276                            + "to target object '" + (target != null ? target.getClass().getName() : null) + "'" + SystemUtils.LINE_SEPARATOR
277                            + "using target fields declared in '" + targetCld.getClassNameOfObject() + "'" + SystemUtils.LINE_SEPARATOR
278                            + "the fields to copy are declared in '" + (targetIsSuper ? targetCld.getClassNameOfObject() : sourceCld.getClassNameOfObject()) + "' class" + SystemUtils.LINE_SEPARATOR
279                            + "the used classes are associated by java inheritance: " + javaInheritance + SystemUtils.LINE_SEPARATOR);
280                    getLog().debug(msg);
281                }
282                /*
283                arminw:
284                If the target object is a super object of the source object, iterate all target object fields.
285                If the source object is a super object of the target object, iterate all source object fields
286    
287                If java inheritance is used (target is super class of source or vice versa) we can use the same
288                FieldDescriptor to copy the fields.
289                If only a "declarative inheritance" is used (no class inheritance, only identical field names of the super class)
290                we have to use the associated FieldDescriptor of target and source ClassDescriptor
291                */
292                FieldDescriptor[] fields = targetIsSuper ? targetCld.getFieldDescriptions() : sourceCld.getFieldDescriptions();
293                for(int i = 0; i < fields.length; i++)
294                {
295                    FieldDescriptor field = fields[i];
296                    if(!field.isAnonymous())
297                    {
298                        performFieldCopy(target,  targetCld, source, sourceCld,
299                                    field.getPersistentField(), targetIsSuper, javaInheritance);
300                    }
301                }
302                List refs = targetIsSuper ? targetCld.getCollectionDescriptors() : sourceCld.getCollectionDescriptors();
303                for(int i = 0; i < refs.size(); i++)
304                {
305                    CollectionDescriptor col = (CollectionDescriptor) refs.get(i);
306                    PersistentField pf = col.getPersistentField();
307                    performFieldCopy(target,  targetCld, source, sourceCld, pf, targetIsSuper, javaInheritance);
308                }
309    
310                refs = targetIsSuper ? targetCld.getObjectReferenceDescriptors() : sourceCld.getObjectReferenceDescriptors();
311                for(int i = 0; i < refs.size(); i++)
312                {
313                    ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) refs.get(i);
314                    PersistentField pf = ord.getPersistentField();
315                    performFieldCopy(target,  targetCld, source, sourceCld, pf, targetIsSuper, javaInheritance);
316                }
317            }
318    
319            private void performFieldCopy(Object target, ClassDescriptor targetCld, Object source,
320                                     ClassDescriptor sourceCld, PersistentField pf, boolean targetIsSuper, boolean javaInheritance)
321            {
322                if(javaInheritance)
323                {
324                    pf.set(target, pf.get(source));
325                }
326                else
327                {
328                    if(targetIsSuper)
329                    {
330                        if(pf instanceof SuperReferenceField)
331                        {
332                            log.error("Declared inheritance doesn't support nested super references, target '"
333                                    + targetCld.getClassNameOfObject() + "' has super reference");
334                        }
335                        else
336                        {
337                            PersistentField tmp = superRef.getDeclaredInheritanceField(sourceCld.getClassOfObject(), pf.getName());
338                            pf.set(target, tmp.get(source));
339                        }
340                    }
341                    else
342                    {
343                        PersistentField tmp = superRef.getDeclaredInheritanceField(targetCld.getClassOfObject(), pf.getName());
344                        tmp.set(target, pf.get(source));
345                    }
346                }
347            }
348        }
349    }
350