KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > xquark > mapper > metadata > PathNode


1 /*
2  * This file belongs to the XQuark distribution.
3  * Copyright (C) 2003 Universite de Versailles Saint-Quentin.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307.
18  * You can also get it at http://www.gnu.org/licenses/lgpl.html
19  *
20  * For more information on this software, see http://www.xquark.org.
21  */

22
23 package org.xquark.mapper.metadata;
24
25
26 import java.sql.SQLException JavaDoc;
27 import java.util.*;
28
29 import org.xml.sax.ContentHandler JavaDoc;
30 import org.xml.sax.SAXException JavaDoc;
31 import org.xml.sax.helpers.AttributesImpl JavaDoc;
32 import org.xquark.mapper.RepositoryException;
33 import org.xquark.mapper.mapping.*;
34 import org.xquark.schema.*;
35 import org.xquark.xpath.*;
36
37 /**
38  * Data structure that corresponds to XML structure paths and allows
39  * direct access to mapping.
40  *
41  */

42 public class PathNode extends TypedXTreeNode implements Cloneable JavaDoc, PathMetadata
43 {
44     private static final String JavaDoc RCSRevision = "$Revision: 1.1 $";
45     private static final String JavaDoc RCSName = "$Name: $";
46     private static final short POTENTIAL = POTENTIAL_STRUCT|DATA_AND_STRUCT;
47     private static final short STRUCT = DATA_AND_STRUCT|STRUCT_ONLY|POTENTIAL_STRUCT;
48     
49     private short pid = RepositoryConstants.ROOT_PATH_ID;
50     private MappingNode mapping;
51     
52     private CollectionMappingInfo readTableMapping;
53     private ColumnMapping readColumnMapping;
54     
55     private CollectionMappingInfo textTableMapping;
56     private ColumnMapping textColumnMapping;
57     
58     private byte storageMode = UNKNOWN; // for compatibility with ROOT OR
59
private boolean childrenOrderEnforced = false; // false when no schema info available
60
private boolean mixed = false;
61     private boolean defaultAllowed = false;
62     /** the first ancestor that allows this node construction
63      */

64     private PathNode builder = null;
65     /** Number min and max of occurrence of this node in its parent node */
66     private int [] occurrenceCount;
67     
68     /** Path node list of decendant-or-self. Cached when asked */
69     private List modelSubPaths = null; // for struct & data
70
private List subPaths = null; // for extra data
71

72     private boolean hasDefaultMappingInScope = false; // TO IMPROVE : for typed default mapping, could be a bitmap for all the tables for basic types
73
// TO CHECK HOW DEFAULT MAPPING IS HANDLED for schemaless and Mixed with optional sub elements
74
private List buildMappings = null; // allocated only if necessary
75

76     public PathNode(PathSet tree, PathNode parent, String JavaDoc namespace, String JavaDoc localName, byte type)
77     {
78         super(tree, parent, namespace, localName, type);
79     }
80     
81     public synchronized short getPathID()
82     {
83         return pid;
84     }
85     
86     public synchronized void setPathID(short pathId)
87     {
88         pid = pathId;
89     }
90     
91     /* to use when a mapping node already exist. Called by RepositoryMapping duplication */
92     private void setMapping(MappingNode node)
93     throws RepositoryException
94     {
95         PathSet pathSet = (PathSet)getTree();
96         
97         // register mapping to mapping to path index
98
if (node.getTableMappings() != null)
99         {
100             Iterator it = node.getTableMappings().iterator();
101             while (it.hasNext())
102                 pathSet.registerMapping((TableMapping)it.next(), this);
103         }
104        
105         mapping = node;
106         setOccurenceCount(); // used by setStorageFlags
107
if ((mapping.getColumnMappings() == null)
108                 && mapping.isDefaultAllowed())
109         {
110             defaultAllowed = true;
111             setDefaultMappingInScope();
112         }
113         else if (mapping.potentialNewPathsExist())
114             setDefaultMappingInScope();
115         setStorageFlags(!pathSet.isLoading());
116         setReadMappings(pathSet.getCollection());
117         
118         if (pathSet.isNew())
119             saveIfNoLoading(pathSet);
120     }
121     
122     private void saveIfNoLoading(PathSet pathSet)
123     throws RepositoryException
124     {
125         if ((pid != RepositoryConstants.ROOT_PATH_ID) && !pathSet.isLoading())
126         {
127             try
128             {
129                 pathSet.store(this);
130             }
131             catch (SQLException JavaDoc e)
132             {
133                 // collision (hope it is)
134
// remove node from the tree and from the path set index
135
remove();
136                 pathSet.freeID(this);
137                 
138                 // eventually, refresh to fetch the new node
139
pathSet.refresh();
140             }
141         }
142     }
143     
144     /* to use when a mapping node does not exist */
145     void setDefaultMapping(MappingFactory mappingFactory, byte type)
146     throws RepositoryException
147     {
148         PathSet pathSet = (PathSet)getTree();
149         // Alocate a new "empty" (no default mapping in it til storage) standalone mapping node
150
mapping = (MappingNode)mappingFactory.allocateNode(
151                     ((PathNode)getParent()).getMapping().getSchemaComponent(),
152                     this
153                     );
154         setOccurenceCount(); // used by setStorageFlags
155
if (mapping.isDefaultAllowed())
156             defaultAllowed = true;
157         setDefaultMappingInScope();
158         
159         setStorageFlags(!pathSet.isLoading());
160         setReadMappings(pathSet.getCollection());
161         
162         saveIfNoLoading(pathSet);
163     }
164     
165     private void setReadMappings(CollectionMetadata collection)
166     throws RepositoryException
167     {
168         ColumnMapping ref = getReferenceColumnMapping();
169         CollectionMappingInfo defMappingInfo =
170             (CollectionMappingInfo)collection.getMappingStatements(collection.getDefaultMappingIndex());
171         TableMapping refTm = getReferenceTableMapping();
172         // read mappings
173
if (ref == null)
174         {
175             if(isDefaultAllowed())// || (storageMode == POTENTIAL_STRUCT))
176
{
177                 readColumnMapping = collection.createDefaultMappingView(getDeclaration());
178                 readTableMapping = defMappingInfo;
179             }
180             else if (refTm != null)
181                 readTableMapping = (CollectionMappingInfo)collection.getMappingStatements(refTm.getTableIndex());
182         }
183         else
184         {
185             if (ref.getTableMapping() == refTm)
186                 readTableMapping = (CollectionMappingInfo)collection.getMappingStatements(refTm.getTableIndex());
187             readColumnMapping = ref;
188         }
189         // text mappings
190
if (isMixed())
191         {
192             textColumnMapping = collection.getDefaultMapping().getDefaultColumnMapping(null);
193             textTableMapping = defMappingInfo;
194         }
195     }
196     
197     private void setOccurenceCount()
198     {
199         Declaration parentDecl, decl = getDeclaration();
200         if (decl == null)
201             occurrenceCount = new int[] {0, Integer.MAX_VALUE}; // Integer.MAX_VALUE is used as unbounded by schema API
202
else
203         {
204             switch (getType())
205             {
206                 case NodeKind.NONE:
207                     occurrenceCount = new int[] {1, 1};
208                     return;
209                     
210                 case NodeKind.ATTRIBUTE:
211                     switch (((AttributeDeclaration)mapping.getSchemaComponent()).getUse())
212                     {
213                         case SchemaConstants.REQUIRED:
214                             occurrenceCount = new int[] {1, 1};
215                             break;
216                         case SchemaConstants.OPTIONAL:
217                             occurrenceCount = new int[] {0, 1};
218                             break;
219                         case SchemaConstants.PROHIBITED:
220                             occurrenceCount = new int[] {0, 0};
221                             break;
222                     }
223                     break;
224                 case NodeKind.ELEMENT:
225                     if ((parentDecl = ((PathNode)getParent()).getDeclaration()) == null)
226                     {
227                         occurrenceCount = new int[] {1, 1};
228                         return;
229                     }
230                     occurrenceCount = ((ComplexType)parentDecl.getType()).getDeclarationOccurrence
231                                     (
232                                     decl.getNamespace(),
233                                     decl.getName()
234                                     );
235                     break;
236             }
237         }
238     }
239     
240     private int[] getOccurrenceCount(int[] occurrence)
241     {
242         long mult;
243         
244         for (int i = 0; i <= 1; i++)
245         {
246             mult = (long)occurrence[i] * (long)occurrenceCount[i];
247             occurrence[i] = (mult <= Integer.MAX_VALUE ? (int)mult : Integer.MAX_VALUE);
248         }
249         return occurrence;
250     }
251     
252     public MappingNode getMapping()
253     {
254         return mapping;
255     }
256     
257     private void setDefaultMappingInScope()
258     {
259         // TO CHECK : setting the flag for node without mapping node ?
260
for (
261             PathNode elt = this;
262             !((elt == null) || elt.hasDefaultMappingInScope);
263             elt = (PathNode)elt.getParent()
264             )
265         {
266             elt.hasDefaultMappingInScope = true;
267         }
268     }
269
270     void setStorageModeFlag(byte storageMode)
271     {
272         this.storageMode = storageMode;
273     }
274     
275     private void setStorageFlags(boolean calculateStorageFlag)
276     {
277         
278         //
279
// BUILDER LINK
280
//
281

282         PathExpr builderLocation = mapping.getBuilder().getLocation();
283         if (builderLocation == null) // <> ROOT or generic mapping (flying node)
284
builder = this;
285         else
286             builder = (PathNode)getTree().getNode(builderLocation);
287         
288         //
289
// mixed FLAG
290
//
291

292         if (getType() == NodeKind.ELEMENT)
293         {
294             if (getDeclaration() == null)
295                 mixed = true;
296             else
297             {
298                 org.xquark.schema.Type type = ((ElementDeclaration)getDeclaration()).getType();
299                 if ((type instanceof ComplexType) &&
300                     (((ComplexType)type).getContentType() == SchemaConstants.MIXED))
301                     mixed = true;
302             }
303         }
304         if (mixed)
305             setDefaultMappingInScope();
306
307         
308         //
309
// STORAGE MODE FLAG
310
//
311

312         if (getType() == NodeKind.NONE)
313             storageMode = DISCARDED;
314         else if(calculateStorageFlag)
315             calculateStorageModeFlag();
316         // else will be read from database
317

318         
319         //
320
// CHILDREN ORDER FLAG
321
//
322
if (getDeclaration() != null) // schema info available
323
{
324             if (((Declaration)getDeclaration()).getType() instanceof SimpleType)
325                 childrenOrderEnforced = true; // meaningless in fact but true in a sense
326
else
327             {
328                 ComplexType type = (ComplexType)((Declaration)getDeclaration()).getType();
329                 switch (type.getContentType())
330                 {
331                     case SchemaConstants.EMPTY: case SchemaConstants.TEXT_ONLY:
332                         childrenOrderEnforced = true; // meaningless in fact but true in a sense
333
break;
334                     case SchemaConstants.MIXED:
335                     case SchemaConstants.ELEMENT_ONLY:
336                         childrenOrderEnforced = browseParticle((Particle)type.getContentModel().getModel(),
337                         new ArrayList(type.getElementDeclarations()));
338                         break;
339                 }
340             }
341         }
342         // else, no schema: default = false
343
}
344     
345     void calculateStorageModeFlag()
346     {
347         // POTENTIAL_STRUCT : schemaless case
348
if (getDeclaration() == null)
349         {
350             if (getType() == NodeKind.ELEMENT)
351                 storageMode = POTENTIAL_STRUCT;
352             else // attribute
353
storageMode = DATA_ONLY;
354         }
355         else
356         {
357 // boolean potentialStruct = false;
358
boolean potentialLeafElement = true; // leaf element, not model-leaf (attributes ignored)
359
boolean potentialModelLeaf = true;
360             boolean modelLeaf = false;
361             boolean hasChildren = false;
362             boolean hasAttributes = false;
363             
364             // Browsing schema info
365
switch (getType())
366             {
367                 case NodeKind.ELEMENT:
368                     if (((ElementDeclaration)getDeclaration()).getType() instanceof ComplexType)
369                     {
370                         ComplexType type = (ComplexType)((ElementDeclaration)getDeclaration()).getType();
371                         switch (type.getContentType())
372                         {
373                             case SchemaConstants.MIXED: case SchemaConstants.ELEMENT_ONLY:
374                                 // sub declarations browsing
375
Iterator it = type.getElementDeclarations().iterator();
376                                 ElementDeclaration decl;
377                                 while (it.hasNext())
378                                 {
379                                     decl = (ElementDeclaration)it.next();
380                                     int[] occurence = type.getDeclarationOccurrence(decl.getNamespace(), decl.getName());
381                                     if (occurence[1] != 0)
382                                         hasChildren = true;
383                                     if (occurence[0] != 0)
384                                         potentialLeafElement = false;
385                                 }
386                             case SchemaConstants.TEXT_ONLY: case SchemaConstants.EMPTY:
387                                 potentialModelLeaf = potentialLeafElement;
388                                 if (type.getAttributeDeclarations() != null)
389                                 {
390                                     it = type.getAttributeDeclarations().iterator();
391                                     int use;
392                                     while (it.hasNext())
393                                     {
394                                         use = ((AttributeDeclaration)it.next()).getUse();
395                                         if (use != SchemaConstants.PROHIBITED)
396                                             hasAttributes = true;
397                                         if(use == SchemaConstants.REQUIRED)
398                                             potentialModelLeaf = false;
399                                     }
400                                 }
401                                 modelLeaf = !hasAttributes && !hasChildren;
402 // potentialStruct = potentialModelLeaf;
403
break;
404                             default:
405                         }
406                     }
407                     else
408                         modelLeaf = true;
409                     break;
410                 case NodeKind.ATTRIBUTE:
411                     modelLeaf = true;
412                     break;
413                 default: // SchemaConstants.EMPTY, SchemaConstants.ELEMENT_ONLY:
414
}
415             
416             // main algorithm (refer to specifications)
417
byte algorithmicStorageMode = DATA_AND_STRUCT;
418             storageMode = DISCARDED; // important in order that getModelAncestor() do not return itself
419
boolean structTuple = true, dataTuple = true;
420             boolean defaultOrText = defaultAllowed || mixed;
421             boolean noTableMapping = (getTableMappings() == null) && !defaultOrText;
422 // if (noTableMapping || (potentialStruct && !potentialLeafElement))
423
if (noTableMapping)
424                 dataTuple = false;
425             if (getModelAncestor() == null) // not to discard both model root and root element
426
structTuple = true;
427             else
428             {
429                 int[] occurrence = getOccurenceCount(getModelAncestor());
430                 if (noTableMapping) // keep struct node only if an intermediate non mono-valuated
431
structTuple = (getColumnMappings() == null)
432                     && ((occurrence[0] == 0) || (occurrence[1] > 1));
433                 else // if not leaf, range necessary
434
{
435                     int tmCount = 0;
436                     if (getTableMappings() != null)
437                         tmCount = getTableMappings().size();
438                     if (defaultOrText && (tmCount == 0) && !modelLeaf)
439                         structTuple = true;
440                     else
441                     {
442                         if (defaultOrText)
443                             tmCount++;
444                         
445                         // builder has been set previously
446
structTuple = !(getBuilderAncestor().getBuildMappings().size() == tmCount);
447                     }
448                 }
449             }
450             
451             if (structTuple)
452             {
453                 if (dataTuple)
454                     algorithmicStorageMode = DATA_AND_STRUCT;
455                 else
456                     algorithmicStorageMode = STRUCT_ONLY;
457             }
458             else if (dataTuple)
459                 algorithmicStorageMode = DATA_ONLY;
460             else
461                 algorithmicStorageMode = DISCARDED;
462             
463             // setting the actual flag : prioritization of rules
464
// -> set the potential struct only if an OID is allocated
465
if (potentialModelLeaf && (algorithmicStorageMode == DATA_AND_STRUCT))
466                 storageMode = POTENTIAL_STRUCT;
467             else
468                 storageMode = algorithmicStorageMode;
469         }
470     }
471     
472     /* false if ALL model group encountered or if an element declaration is
473      * found in multiple particules in the content model.
474      */

475     private boolean browseParticle(Particle particle, Collection elementDecls)
476     {
477         
478         if (particle.getTerm() instanceof Wildcard)
479             return false;
480         else if (particle.getTerm() instanceof ElementDeclaration)
481         {
482             if (!elementDecls.remove(particle.getTerm()))
483                 return false; // not the first time it is removed
484
}
485         else
486         {
487             ModelGroup modelGroup = (ModelGroup)particle.getTerm();
488             if (modelGroup.getCompositor() == ModelGroup.ALL)
489                 return false;
490             
491             Iterator it = ((ModelGroup)particle.getTerm()).iterator();
492             boolean ret = true;
493             
494             while (it.hasNext())
495             {
496                 if (!browseParticle((Particle)it.next(), elementDecls))
497                     return false;
498             }
499         }
500         return true;
501     }
502     
503     /* Add customization to the nodes created by duplicate */
504     protected XTreeNode customizeDuplicate(XTreeNode original)
505     {
506         PathSet pathSet = (PathSet)getTree();
507         try {
508             setMapping((MappingNode)original);
509         }
510         catch (RepositoryException e) {
511             throw new XTreeRuntimeException(e);
512         }
513         return this;
514     }
515     
516     public String JavaDoc toString()
517     {
518         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
519         buf.append(getExpandedName());
520         buf.append(" [pid=");
521         buf.append(getPathID());
522         buf.append(",rm=");
523         buf.append(getReferenceColumnMapping());
524         buf.append(",sm=");
525         buf.append(storageMode);
526         buf.append(",co=");
527         buf.append(childrenOrderEnforced);
528         buf.append(",ni=");
529         buf.append(isNilImplicit());
530         buf.append(",dma=");
531         buf.append(mapping.isDefaultAllowed());
532         buf.append(",dmis=");
533         buf.append(hasDefaultMappingInScope);
534         buf.append("]");
535
536         return buf.toString();
537     }
538
539     ////////////////////////////////////////////////////////////////////////////
540
// PathMetadata implementation
541
////////////////////////////////////////////////////////////////////////////
542

543      /**
544       * Get the minimum & maximum number of occurences of this path
545       * in an ancestor.
546       * @param ancestor the ancestor PathMetadata node which the occurrence is
547       * calculated in . If the parameter node is not
548       * encountered as one of the current node ancestor,
549       * calculation is performed relatively to the metadata
550       * tree root.
551       * @return a two int arrays where first if the minimum number of
552       * occurrence, and the second is the maximum.
553       */

554     public int[] getOccurenceCount(PathMetadata ancestor)
555     {
556         int[] occurrence = {1,1};
557         
558         for(PathNode current = this;
559             (current.getPathID() != ancestor.getPathID()) && (current.getPathID() != RepositoryConstants.ROOT_PATH_ID);
560             current = (PathNode)current.getParent())
561         {
562             current.getOccurrenceCount(occurrence);
563         }
564         return occurrence;
565     }
566     
567     public boolean isDefaultAllowed()
568     {
569         return defaultAllowed;
570     }
571     
572     /**
573      * Get the MetadataNode on which the table mapping corresponding to
574      * the 'Read' column mapping is defined.
575      * @return null if no column mapping is defined on the current path
576      */

577     public PathMetadata getTableMappingPath()
578     {
579         ColumnMapping cm = getReferenceColumnMapping();
580         if (cm == null)
581             return this;
582         else
583             return getTableMappingPath(cm);
584     }
585     
586     /**
587      * Get the MetadataNode on which the table mapping corresponding to the
588      * specified column mapping is defined.
589      * @param column the column mapping which the table mapping path is searched
590      * for
591      * @return the path of the node on which the table mapping is defined
592      * @see org.xquark.mapper.mapping.ColumnMapping
593      */

594     public PathMetadata getTableMappingPath(ColumnMapping column)
595     {
596         PathNode node = this;
597         if (column == null)
598             return null;
599         while((node.getTableMappings() == null)
600             || !node.getTableMappings().contains(column.getTableMapping()))
601         {
602             node = (PathNode)node.getParent();
603         }
604         return node;
605     }
606     
607     public PathMetadata getBuildableAncestor()
608     {
609         PathNode node = this;
610         while(!node.getMapping().isBuildable())
611         {
612             node = (PathNode)node.getParent();
613         }
614         return node;
615     }
616     
617     /**
618      * Get the first ancestor having in scope all the tables to build the
619      * current node (even if itself is not buildable).
620      * @return a PathMetadata node.
621      */

622     public PathMetadata getBuilderAncestor()
623     {
624         return builder;
625     }
626     
627     /**
628      * Get the first ancestor having an OID.
629      * @return a PathMetadata node.
630      */

631     public PathMetadata getModelAncestor()
632     {
633         PathNode node = this;
634         while((node.getType() != NodeKind.NONE) && (node.getStorageMode() == DISCARDED))
635         {
636             node = (PathNode)node.getParent();
637         }
638         if (node.getType() == NodeKind.NONE)
639             return null;
640         else
641             return node;
642     }
643     
644     /**
645      * Get the first ancestor having a structure tuple.
646      * <I>Potentially useless.</I>
647      * @return a PathMetadata node.
648      */

649     public PathMetadata getStructAncestor()
650     {
651         // Process the POTENTIAL_STRUCT case
652
if ((getStorageMode() == POTENTIAL_STRUCT) && !isLeaf())
653             return this;
654         
655         PathNode node = this;
656         while((node.getStorageMode() & STRUCT) == 0)
657         {
658             node = (PathNode)node.getParent();
659         }
660         return node;
661     }
662     
663     /** Indicate if looking in the extra-data is necessary to determine the
664      * missing/nil status for a node.
665      * @return True if a check is needed.
666      */

667     public boolean isNilImplicit()
668     {
669         // schemaless: nil has no meaning and no optimization is performed
670
if (getDeclaration() == null)
671             return false;
672         
673         boolean nillable = false, optional = false;
674         switch (getType())
675         {
676             case NodeKind.ATTRIBUTE :
677                 optional = (((AttributeDeclaration)getDeclaration()).getUse() == SchemaConstants.OPTIONAL);
678                 break;
679             case NodeKind.ELEMENT :
680                 ElementDeclaration decl = (ElementDeclaration)getDeclaration();
681                 optional = (occurrenceCount[0] == 0);
682                 nillable = decl.isNillable();
683                 break;
684         }
685         
686         return ((getStorageMode() == DISCARDED)
687                 && (getReferenceColumnMapping() != null)
688                 && (nillable && !optional));
689         // TO IMPROVE : PRECOMPILE
690
}
691     
692     public boolean hasExtraData()
693     {
694         return true; // TO IMPROVE: in a first step, no dynamic flag is maintained in PathSet
695
}
696     
697     /** Indicate the storage mode of the node, i.e., if the node is
698      * stored and where it is stored.
699      * @return a constant for all possible storage scheme
700      * @see #DISCARDED
701      * @see #POTENTIAL_STRUCT
702      * @see #STRUCT_ONLY
703      * @see #DATA_ONLY
704      * @see #DATA_AND_STRUCT
705      */

706     public byte getStorageMode()
707     {
708         return storageMode;
709     }
710     
711     /** Flag to indicate if the node can be removed from the storage
712      * model (no OID allocated).
713      * i.e., getStorageMode() == DISCARDED
714      * @return true if node can be optimized
715      */

716     public boolean isDiscardable()
717     {
718         return getStorageMode() == DISCARDED;
719     }
720     
721     /** Flag to indicate if the node has been stored and an OID allocated.
722      * <p>i.e., getStorageMode() != DISCARDED</p>
723      * @return true if an OID exists for the node
724      */

725     public boolean isPersistent()
726     {
727         return getStorageMode() != DISCARDED;
728     }
729     
730     public ColumnMapping getReferenceColumnMapping()
731     {
732         return mapping.getReferenceColumnMapping();
733     }
734     
735     public TableMapping getReferenceTableMapping()
736     {
737         return mapping.getReferenceTableMapping();
738     }
739     
740      /**
741       * Get the column mappings defined on this path.
742       * @return a list of column mappings
743       * @see org.xquark.mapper.mapping.ColumnMapping
744       */

745      public List getColumnMappings()
746      {
747          return mapping.getColumnMappings();
748      }
749      
750     /**
751      * Get the column mapping corresponding to one of the table mappings
752      * passed as a parameter.
753      * @param tableMappingIndexes an array containing the mapping numerical IDs
754      * (index) of table mappings
755      * @return the reference one if no table or multiple mapping match. Null if
756      * no column mappings available
757      * @see org.xquark.mapper.mapping.TableMapping
758      * @see org.xquark.mapper.mapping.ColumnMapping
759      */

760     public ColumnMapping getColumnMapping(int[] tableMappingIndexes)
761     {
762         ColumnMapping ret = null, column;
763         if (mapping.getColumnMappings() == null)
764             return null;
765         
766         Iterator it = mapping.getColumnMappings().iterator();
767         while (it.hasNext())
768         {
769             column = (ColumnMapping)it.next();
770             if (Arrays.binarySearch(tableMappingIndexes, column.getTableMapping().getTableIndex()) < 0)
771             {
772                 if (ret == null)
773                     ret = column;
774                 else
775                     return getReferenceColumnMapping();
776             }
777         }
778         return ret;
779     }
780     
781     /**
782      * Get the table mappings defined on this path.
783      * @return a list of table mappings
784      * @see org.xquark.mapper.mapping.TableMapping
785      */

786     public List getTableMappings()
787     {
788         return mapping.getTableMappings();
789     }
790     
791     /** Get the table mappings in the scope of the element (default included
792      * if necessary) needed to build itself or a subpath.
793      * <p>To be used after getBuilderAncestor() or getBuildableAncestor().</p>
794      * @return a collection of TableMapping objects or null (if no mapping).
795      */

796     public Collection getBuildMappings()
797     {
798         if (hasDefaultMappingInScope)
799         {
800             if (buildMappings == null)
801             {
802                 if (mapping.getScopeMappings() == null)
803                     buildMappings = (((PathSet)getTree()).getCollection()).getDefaultMappingList();
804                 else
805                 {
806                     buildMappings = new ArrayList(mapping.getScopeMappings());
807                     buildMappings.add((((PathSet)getTree()).getCollection()).getDefaultMapping());
808                 }
809             }
810             return buildMappings;
811         }
812         else
813             return mapping.getScopeMappings();
814     }
815     
816     /** Returns the schema component for this node.
817      * <p>Allows to access declarations info like nillable, minOccurs...</p>
818      * @return The schema component corresponding to the path if any.
819      * Null else.
820      */

821     public Declaration getDeclaration()
822     {
823         return mapping.getDeclaration();
824     }
825     
826     /** Indicate if the position of the node iwithin its siblings is enforced
827      * by schema information.
828      * @return True if positon of the node can be enforced by schema information.
829      */

830     public boolean isPositionEnforced()
831     {
832         if (getType() == NodeKind.ATTRIBUTE)
833             return true; // order ins meaningless and not guaranteed
834
else
835         {
836             PathNode parent = (PathNode)getParent();
837             return parent.childrenOrderEnforced && !parent.mixed;
838         }
839     }
840     
841     public boolean isChildrenPositionEnforced()
842     {
843         return childrenOrderEnforced;
844     }
845     
846     protected void toXML(ContentHandler JavaDoc handler)
847     throws SAXException JavaDoc
848     {
849         AttributesImpl JavaDoc atts = new AttributesImpl JavaDoc();
850         String JavaDoc value;
851         
852         handler.startElement("", "data", "", atts);
853         
854         // Path ID
855
handler.startElement("", "pid", "", atts);
856         value = Short.toString(pid);
857         handler.characters(value.toCharArray(), 0, value.length());
858         handler.endElement("", "pid", "");
859         
860         // Occurence count
861
atts.addAttribute("", "min", "", "CDATA", String.valueOf(occurrenceCount[0]));
862         atts.addAttribute("", "max", "", "CDATA", String.valueOf(occurrenceCount[1]));
863         handler.startElement("", "occurrenceCount", "", atts);
864         atts.clear();
865         handler.endElement("", "occurrenceCount", "");
866         
867         // Builder path
868
handler.startElement("", "builder", "", atts);
869         value = Short.toString(builder.getPathID());
870         handler.characters(value.toCharArray(), 0, value.length());
871         handler.endElement("", "builder", "");
872         
873         // Model ancestor path (anchor)
874
PathMetadata modelAncestor = getModelAncestor();
875         if (modelAncestor != null)
876         {
877             handler.startElement("", "anchor", "", atts);
878             value = Short.toString(modelAncestor.getPathID());
879             handler.characters(value.toCharArray(), 0, value.length());
880             handler.endElement("", "anchor", "");
881         }
882        
883         // Optimization flags
884
switch (storageMode)
885         {
886             case UNKNOWN:
887                 value = "unknown";
888                 break;
889             case DISCARDED:
890                 value = "discarded";
891                 break;
892             case POTENTIAL_STRUCT:
893                 value = "potential struct";
894                 break;
895             case STRUCT_ONLY:
896                 value = "struct only";
897                 break;
898             case DATA_ONLY:
899                 value = "data only";
900                 break;
901             case DATA_AND_STRUCT:
902                 value = "data & struct";
903                 break;
904         }
905         atts.addAttribute("", "storageMode", "", "CDATA", value);
906         atts.addAttribute("", "childrenOrderEnforced", "", "CDATA", String.valueOf(childrenOrderEnforced));
907         atts.addAttribute("", "positionEnforced", "", "CDATA", String.valueOf(isPositionEnforced()));
908         atts.addAttribute("", "nilImplicit", "", "CDATA", String.valueOf(isNilImplicit()));
909         atts.addAttribute("", "hasDefaultMappingInScope", "", "CDATA", String.valueOf(hasDefaultMappingInScope));
910         atts.addAttribute("", "mixed", "", "CDATA", String.valueOf(isMixed()));
911         atts.addAttribute("", "defaultAllowed", "", "CDATA", String.valueOf(isDefaultAllowed()));
912         handler.startElement("", "flags", "", atts);
913         atts.clear();
914         handler.endElement("", "flags", "");
915         
916         handler.startElement("", "readTable", "", atts);
917         if (readTableMapping != null)
918         {
919             value = readTableMapping.getTableMapping().getTableName();
920             handler.characters(value.toCharArray(), 0, value.length());
921         }
922         handler.endElement("", "readTable", "");
923         
924         handler.startElement("", "readColumn", "", atts);
925         if (readColumnMapping != null)
926         {
927             value = readColumnMapping.getTableName() + "." + readColumnMapping.getColumnName();
928             handler.characters(value.toCharArray(), 0, value.length());
929         }
930         handler.endElement("", "readColumn", "");
931     
932         handler.startElement("", "mapping", "", atts);
933         mapping.toXML(handler);
934         handler.endElement("", "mapping", "");
935         
936         handler.endElement("", "data", "");
937     }
938     
939     /** Return true if the element can be missing if its parent exists.
940      * @return true if optional
941      */

942     public boolean isOptional()
943     {
944         return occurrenceCount[0] == 0;
945     }
946     
947     /** Return true if the element can only appear one time in its parent.
948      * @return true if monovaluated
949      */

950     public boolean isMonoValuated()
951     {
952         return occurrenceCount[1] == 1;
953     }
954     
955     /** Return true if the element can appear several times in its parent.
956      * @return true if multivaluated
957      */

958     public boolean isMultiValuated()
959     {
960         return occurrenceCount[1] > 1;
961     }
962     
963     public void invalidateCache()
964     {
965         modelSubPaths = null;
966         subPaths = null;
967         if (parent != null)
968             ((PathNode)parent).invalidateCache();
969     }
970     
971     /** Return a list of the path nodes with an OID contained in the subtree.
972      * @return null if the path is of root type.
973      */

974     public List getModelSubPaths()
975     {
976         // TO SEE : could precompile it and update when pathset is modified
977
if (modelSubPaths == null)
978             browseSubPaths();
979         
980         return modelSubPaths;
981     }
982     
983     /** Return a list of the path nodes with or without an OID contained in
984      * the subtree.
985      * @return null if the path is of root type.
986      */

987     public List getSubPaths()
988     {
989         // TO SEE : could precompile it and update when pathset is modified
990
if (subPaths == null)
991             browseSubPaths();
992         
993         return subPaths;
994     }
995     
996     private void browseSubPaths()
997     {
998         // TO SEE : could precompile it and update when pathset is modified. Better : use range for paths too
999
if (getType() == NodeKind.NONE)
1000            return;
1001        else if (isLeaf())
1002        {
1003            subPaths = Collections.singletonList(this);
1004            if (isPersistent())
1005                modelSubPaths = subPaths;
1006            else
1007                modelSubPaths = Collections.EMPTY_LIST;
1008        }
1009        else
1010        {
1011            Iterator it = new XTreeIterator(this);
1012            PathNode node;
1013            modelSubPaths = new ArrayList();
1014            subPaths = new ArrayList();
1015            
1016            // process first itself
1017
// if (isPersistent())
1018
// modelSubPaths.add(this);
1019
// subPaths.add(this);
1020

1021            while (it.hasNext())
1022            {
1023                node = (PathNode)it.next();
1024                if (node.isPersistent())
1025                    modelSubPaths.add(node);
1026                subPaths.add(node);
1027            }
1028        }
1029    }
1030    
1031    /** get column mapping used for reconstruction and queries (that may be
1032     * different than the user-defined ones */

1033    public ColumnMapping getReadColumnMapping()
1034    {
1035        return readColumnMapping;
1036    }
1037    
1038    /** get Table mapping used for reconstruction and queries (that may be
1039     * different than the user-defined ones */

1040    public CollectionMappingInfo getReadTableMapping()
1041    {
1042        return readTableMapping;
1043    }
1044    
1045    /** Shortcut to the schema content type.
1046     * @return true if elemnt is mixed.
1047     */

1048    public boolean isMixed()
1049    {
1050        return mixed;
1051    }
1052    
1053    /** get column mapping used for text nodes. This method returns a value only
1054     * if {@link isMixed()} is true
1055     */

1056    public ColumnMapping getTextColumnMapping()
1057    {
1058        return textColumnMapping;
1059    }
1060    
1061    /** get table mapping used for text nodes. This method returns a value only
1062     * if {@link isMixed()} is true
1063     */

1064    public CollectionMappingInfo getTextTableMapping()
1065    {
1066        return textTableMapping;
1067    }
1068    
1069    /** get table mapping information customized for this pathset collection.
1070     */

1071    public CollectionMappingInfo getTableMappingInfo(TableMapping mapping)
1072    {
1073        return (CollectionMappingInfo)((PathSet)tree).getCollection().getMappingStatements(mapping.getTableIndex());
1074    }
1075    
1076}
1077
Popular Tags