View Javadoc

1   /* (c) Copyright 2003 Caleigo AB, All rights reserved. 
2    * 
3    * This library is free software; you can redistribute it and/or
4    * modify it under the terms of the GNU Lesser General Public
5    * License as published by the Free Software Foundation; either
6    * version 2.1 of the License, or (at your option) any later version.
7    * 
8    * This library is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   * Lesser General Public License for more details.
12   * 
13   * You should have received a copy of the GNU Lesser General Public
14   * License along with this library; if not, write to the Free Software
15   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16   *  
17   */
18  
19  package org.caleigo.core;
20  
21  import java.util.*;
22  
23  import org.caleigo.core.exception.*;
24  
25  /*** <Description for Relations>
26   *
27   * @author  Dennis Zikovic
28   * @version 1.00
29   *
30   *//* 
31   *
32   * WHEN        WHO               WHY & WHAT
33   * -----------------------------------------------------------------------------
34   * 2002-05-14  Dennis Zikovic    Creation
35   */
36  public final class Relations 
37  {
38      // Constructors ------------------------------------------------------------
39      
40      /*** Constructor is private to avoid inproper instatiations.
41       */
42      private Relations() 
43      {
44      }
45      
46      // Static methods ----------------------------------------------------------
47      
48      public static boolean isIntersection(IEntityDescriptor descriptor1, IEntityDescriptor descriptor2)
49      {
50          boolean found = false;
51          for(int j=0; j<descriptor1.getFieldCount() && !found; j++)
52              found = descriptor2.contains(descriptor1.getFieldDescriptor(j));
53          return found;
54      }
55      
56      public static boolean isIdentityIntersection(IEntityDescriptor descriptor1, IEntityDescriptor descriptor2)
57      {
58          boolean found = descriptor1==descriptor2;
59          IFieldDescriptor[] identityFields = Relations.getIdentityFields(descriptor1);
60          for(int j=0; j<identityFields.length && !found; j++)
61              found = descriptor2.contains(identityFields[j]);
62          return found;
63      }
64          
65      /*** Returns true if the provided entity descriptors are related.
66       */
67      public static boolean isRelated(IEntityDescriptor startDescriptor, IEntityDescriptor endDescriptor)
68      {
69          try
70          {
71              return EntityRelationPath.create(startDescriptor, endDescriptor)!=null;
72          }
73          catch(InvalidRelationException e) 
74          {
75              return false;
76          }
77      }
78      
79      /*** Returns true if the provided entity descriptors are reference related,
80       * meaning that there can be endDescriptor entity for any given 
81       * startDescriptor entity.
82       */
83      public static boolean isReferenceRelated(IEntityDescriptor startDescriptor, IEntityDescriptor endDescriptor)
84      {
85          try
86          {
87              IEntityRelationPath path = EntityRelationPath.create(startDescriptor, endDescriptor);
88              return path.getForwardCardinality()==IEntityRelationPath.ONE;
89          }
90          catch(InvalidRelationException e) 
91          {
92              return false;
93          }
94      }
95      
96      /*** Returns true if the provided entity descriptor is related to other
97       * entity descriptors as the target of the relation. The method can used
98       * to determine if it is safe to delete or change an etity's identity fields
99       * whitout the risk of causing a foreign key violation in slave entities.
100      */
101     public static boolean isReferenceTarget(IEntityDescriptor descriptor)
102     {
103         for(int j=0; j<descriptor.getEntityRelationCount(); j++)
104             if(descriptor.getEntityRelation(j).canBeTarget(descriptor))
105                 return true;
106         return false;
107     }
108     
109     /*** Returns true if the provided relation path is a "link" relation meaning
110      * that the path is defined by two and only two entity relations with
111      * an entity descriptor of the type LINK_ENTITY between the relations.
112      */
113     public static boolean isLinkRelation(IEntityRelationPath relationPath)
114     {
115         if(relationPath.getRelationCount()==2 && relationPath.getRelation(0).getRelatedEntityDescriptor(relationPath.getFirstEntityDescriptor()).getEntityType()==IEntityDescriptor.LINK_ENTITY)
116             return true;
117         else
118             return false;
119     }
120 
121     public static int getIdentityFieldCount(IEntityDescriptor entityDescriptor)
122     {
123         int count = 0;
124         for(int j=0; j<entityDescriptor.getFieldCount(); j++)
125             if(entityDescriptor.getFieldDescriptor(j).isIdentityField())
126                 count++;
127         return count;
128     }
129     
130     public static IFieldDescriptor[] getIdentityFields(IEntityDescriptor entityDescriptor)
131     {
132         int count = getIdentityFieldCount(entityDescriptor);
133         
134         IFieldDescriptor[] fields = new IFieldDescriptor[count];
135         for(int j=entityDescriptor.getFieldCount()-1; j>=0; j--)
136             if(entityDescriptor.getFieldDescriptor(j).isIdentityField())
137                 fields[--count] = entityDescriptor.getFieldDescriptor(j);
138         
139         return fields;
140     }
141     
142     /*** creates a subpath of the provided entity relation path starting at
143      * the provided index to the end of the provided path.
144      */
145     public static IEntityRelationPath getSubRelationPath(IEntityRelationPath path, int start)
146     {
147         return getSubRelationPath(path, start, path.getRelationCount()-1);
148     }
149 
150     /*** creates a subpath of the provided entity relation path starting at
151      * the start-index and ending at tha end-index.
152      */
153     public static IEntityRelationPath getSubRelationPath(IEntityRelationPath path, int start, int end)
154     {
155         if(start<0 || end>=path.getRelationCount() || start>end)
156             throw new IllegalArgumentException();
157         
158         EntityRelationPath relationPath = new EntityRelationPath(path.getRelation(start), path.isRelationByReference(start));
159         for(int j=start+1; start<end; j++)
160             relationPath.appendRelation(path.getRelation(j), path.isRelationByReference(j));
161 
162         return relationPath;
163     }
164 
165     public static IEntity loadReferredEntity(IEntity sourceEntity, IFieldDescriptor referenceFieldDescriptor)
166     {
167         IFieldRelation refRelation = referenceFieldDescriptor.getReferenceFieldRelation();
168         if(refRelation==null)
169             return null;
170         
171         Qualifier refQualifier = null;
172         try
173         {
174             Iterator it = refRelation.getEntityRelation().getFieldRelations();
175             while(it.hasNext())
176             {
177                 refRelation = (IFieldRelation)it.next();
178                 refQualifier = Qualifier.combine(refQualifier, Qualifier.create(refRelation.getTargetField(), sourceEntity.getData(refRelation.getReferenceField())));
179             }
180         }
181         catch(InvalidFieldException e)
182         {
183             return null;
184         }
185                         
186         if(refQualifier!=null)
187             return refRelation.getTargetField().getEntityDescriptor().loadEntity(refQualifier);
188         else
189             return null;
190     }
191     
192     public static ISelection loadRelatedEntities(IEntity sourceEntity, IEntityDescriptor targetDescriptor)
193     {
194         return loadRelatedEntities(sourceEntity, targetDescriptor, null);
195     }
196     
197     public static ISelection loadRelatedEntities(IEntity sourceEntity, IEntityDescriptor targetDescriptor, IFieldDescriptor[] wayPoints)
198     {
199         IEntityRelationPath path = EntityRelationPath.create(sourceEntity.getEntityDescriptor(), wayPoints, targetDescriptor);
200         return targetDescriptor.loadSelection(sourceEntity.getOriginQualifier().and(path.getRelationQualifier()));
201     }
202     
203     /*** Loads an uniquely identifiable entity based on the data in the source. 
204      * entity. If no such entity can logicaly be identified null is rerturned.
205      */
206     public static IEntity loadIntersectEntity(IEntity sourceEntity, IEntityDescriptor targetDescriptor)
207     {
208         Qualifier identityQualifier = makeIdentityQualifier(sourceEntity, targetDescriptor);
209         if(identityQualifier!=null)
210             return targetDescriptor.loadEntity(identityQualifier);
211         else
212             return null;
213     }
214     
215     /*** Makes a qualifer of the provided field descriptors and entity. 
216      * The qualifier will use relation qualifers using EQUAL to qualify
217      * the field data. The relation qualifiers will be placed in a composite
218      * qualifier using INTERSECTION. If not all fields could be qualified by 
219      * the provided entity null is returned.
220      * <p>
221      * The fields must be directly conatined in the entity or have refernece
222      * fields in the entity that directly refers to a provided field.
223      */
224     public static Qualifier makeFieldQualifier(IFieldDescriptor[] descriptors, IEntity entity)
225     {
226         Object[] fieldData = new Object[descriptors.length];
227         int foundCount = 0;
228         
229         // Get data from directly contained fields.
230         for(int j=0; j<descriptors.length; j++)
231             if(entity.getEntityDescriptor().contains(descriptors[j]))
232             {
233                 fieldData[j] = entity.getData(descriptors[j]);
234                 foundCount++;
235             }
236         
237         // Get dat data from directly referenced fields.
238         if(foundCount<descriptors.length)
239         {
240             IEntityDescriptor entityDescriptor = entity.getEntityDescriptor();
241             for(int j=0; j<descriptors.length; j++)
242             {
243                 for(int k=0; fieldData[j]==null && k<entityDescriptor.getFieldCount(); k++)
244                 {
245                     if(entityDescriptor.getFieldDescriptor(k).isReferenceField()
246                             && entityDescriptor.getFieldDescriptor(k).getReferenceFieldRelation().getTargetField()==descriptors[j])
247                     {
248                         fieldData[j] = entity.getData(entityDescriptor.getFieldDescriptor(k));
249                         foundCount++;
250                     }
251                 }
252             }
253         }
254         
255         // Make and return qualifier.
256         if(foundCount==descriptors.length)
257         {
258             Qualifier qualifier = null;
259             for(int j=0; j<descriptors.length; j++)
260                 qualifier = Qualifier.combine(qualifier, Qualifier.create(descriptors[j], fieldData[j]));
261             return qualifier;              
262         }
263         else
264             return null;
265     }
266         
267     /*** Makes an identity qualifier for an entity of the targetDescriptor type
268      * using the fields in the sourceEntity. If not all identity fields can be
269      * satisfied a null value is returned.
270      */
271     public static Qualifier makeIdentityQualifier(IEntity sourceEntity, IEntityDescriptor targetDescriptor)
272     {
273         if(sourceEntity.getEntityDescriptor()==targetDescriptor)
274             return sourceEntity.getOriginQualifier();
275         else
276             return makeFieldQualifier(getIdentityFields(targetDescriptor), sourceEntity);
277     }
278     
279     /*** Makes a qualifier that qualifies entities of the oposite end of the
280      * provided relation object. Qualification data will be taken from the
281      * provided source entity. The method can be used to resolve single step
282      * relations by direclty qualifiyng qualifier.
283      */
284     public static Qualifier makeRelationQualifier(IEntity sourceEntity, IEntityRelation entityRelation)
285     {
286         if(entityRelation==null || !entityRelation.isRelationNode(sourceEntity.getEntityDescriptor()))
287             throw new InvalidRelationException("Entity relation not related to "+sourceEntity);
288         
289         Qualifier qualifier = null;
290         java.util.Iterator it = entityRelation.getFieldRelations();
291         boolean sourceIsTarget = entityRelation.getTargetEntityDescriptor()==sourceEntity.getEntityDescriptor();
292         while(it.hasNext())
293         {
294             IFieldRelation fieldRelation = (IFieldRelation)it.next();
295             if(sourceIsTarget)
296                 qualifier = Qualifier.combine(qualifier, Qualifier.create(fieldRelation.getReferenceField(), sourceEntity.getData(fieldRelation.getTargetField())));
297             else
298                 qualifier = Qualifier.combine(qualifier, Qualifier.create(fieldRelation.getTargetField(), sourceEntity.getData(fieldRelation.getReferenceField())));
299         }
300         return qualifier;
301     }
302     
303     /*** Sets all field data that can be extracted from the source data entity
304      * using the provided relation. If the provided relation is null then the 
305      * first usable direct relation between the entities is used.
306      */
307     public static void setReferenceData(IEntity dataTargetEntity, IEntity dataSourceEntity, IEntityRelation relation)
308     {
309         // Select first usable relation if none is provided.
310         for(int j=0; relation==null && j<dataTargetEntity.getEntityDescriptor().getEntityRelationCount(); j++)
311             if(dataTargetEntity.getEntityDescriptor().getEntityRelation(j).canBeReference(dataTargetEntity.getEntityDescriptor())
312                     && dataTargetEntity.getEntityDescriptor().getEntityRelation(j).canBeTarget(dataSourceEntity.getEntityDescriptor()))
313                 relation = dataTargetEntity.getEntityDescriptor().getEntityRelation(j);
314         
315         // Validate relation.
316         if(relation==null || !relation.isRelationNode(dataSourceEntity.getEntityDescriptor()))
317             throw new InvalidRelationException("Entity relation not valid "+relation);
318         
319         // Copy relation data from source to target
320         for(int j=0; j<relation.getFieldCount(); j++)
321         {
322             if(!dataSourceEntity.isPersistent())
323             {
324                 if(dataSourceEntity instanceof IProxyEntity)
325                     dataTargetEntity.setData(relation.getFieldRelation(j).getReferenceField(), new EntityFieldProxyData(relation.getFieldRelation(j).getTargetField(), ((IProxyEntity)dataSourceEntity).getSourceEntity()));
326                 else
327                     dataTargetEntity.setData(relation.getFieldRelation(j).getReferenceField(), new EntityFieldProxyData(relation.getFieldRelation(j).getTargetField(), dataSourceEntity));
328             }
329             else
330                 dataTargetEntity.setData(relation.getFieldRelation(j).getReferenceField(), dataSourceEntity.getData(relation.getFieldRelation(j).getTargetField()));
331         }
332     }
333 }
334 
335 
336 
337