KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > xquark > mapper > storage > ModelBuilder


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 /*
24  * LogicalStructureBuilder.java
25  *
26  * Created on 25 juillet 2001, 11:45
27  */

28
29 package org.xquark.mapper.storage;
30
31 import java.sql.PreparedStatement JavaDoc;
32 import java.sql.SQLException JavaDoc;
33 import java.util.Collections JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.List JavaDoc;
36
37 import org.xml.sax.Locator JavaDoc;
38 import org.xquark.mapper.RepositoryException;
39 import org.xquark.mapper.dbms.*;
40 import org.xquark.mapper.mapping.ColumnMapping;
41 import org.xquark.mapper.metadata.*;
42 import org.xquark.mapper.util.RecyclingStack;
43 import org.xquark.mapper.util.RepositoryProperties;
44 import org.xquark.schema.SchemaConstants;
45 import org.xquark.schema.validation.ElementPSVInfoset;
46 import org.xquark.schema.validation.PSVInfoset;
47 import org.xquark.schema.validation.SchemaValidationContext;
48 import org.xquark.xml.xdbc.XMLDBCException;
49 import org.xquark.xpath.NodeKind;
50
51 /**
52  *
53  */

54 class ModelBuilder extends AbstractModelBuilder
55 {
56     private static final String JavaDoc RCSRevision = "$Revision: 1.1 $";
57     private static final String JavaDoc RCSName = "$Name: $";
58     
59     private static final short NO_STRUCT = StoragePathMetadata.DISCARDED|StoragePathMetadata.DATA_ONLY;
60     private static final short POTENTIAL_DATA = StoragePathMetadata.POTENTIAL_STRUCT|StoragePathMetadata.DATA_AND_STRUCT|StoragePathMetadata.DATA_ONLY;
61     
62     protected PreparedStatement JavaDoc idStmt;
63     
64     private long lastOID = -1;
65     private long nodeCount = 0;
66     private long docDataSize = 0;
67     
68     private long DID = -1;
69     private long UDID = -1;
70     
71     private int extraDataMaxSize = -1;
72     private String JavaDoc docID = null;
73     private short rootPosition = 0;
74     private short rowNum = 0;
75     private ExtraNode wExtraNode = new ExtraNode();
76     private UOIDManager manager;
77     
78     // XQuark index persistency
79
StructSaver nodeRow;
80     ExtraDataSaver extraNodeRow;
81     
82     // Mapping information variables
83
List JavaDoc textTableMappingList = null;
84     List JavaDoc textColumnMappingList = null;
85     
86     public ModelBuilder(_RepositoryCollection collection, SchemaValidationContext schemaContext)
87     throws XMLDBCException
88     {
89         super(collection.getRepositoryConnection().getConnection(), schemaContext);
90         try
91         {
92             initBatcher(batcher, collection.getMetadata());
93         }
94         catch (SQLException JavaDoc e)
95         {
96             throw new RepositoryException(RepositoryException.DB_ERROR, "JDBC error while creating statement for documents IDs.", e);
97         }
98         AbstractConnection conn = collection.getRepositoryConnection().getConnection();
99         CollectionMetadata meta = collection.getMetadata();
100         manager = meta.getUOIDManager();
101         storageBuffer = new StorageBuffer
102                                         (
103                                         meta,
104                                         meta.getCollectionName(),
105                                         conn,
106                                         batcher,
107                                         this,
108                                         !conn.distinguishNullAndEmptyStrings()
109                                         );
110         ColumnMapping textColumnMapping = meta.createDefaultMappingView(null);
111         textColumnMappingList = Collections.singletonList(textColumnMapping);
112         textTableMappingList = Collections.singletonList(textColumnMapping.getTableMapping());
113         extraDataMaxSize = collection.getInfo().getMaxExtraDataLength();
114     }
115     
116     private void initBatcher(JDBCBatcher batcher, CollectionMetadata collection) throws SQLException JavaDoc
117     {
118         // doc list table
119
TableInfo table = collection.getTableInfo(TableSpec.TYPE_DOCS);
120         idStmt = connection.getConnection().prepareStatement(table.getInsertStatement());
121         batcher.addStatement(idStmt, table.getName(), RepositoryProperties.getIntProperty(CONF_DOCID_BATCHSIZE));
122         
123         // structure table
124
table = collection.getTableInfo(TableSpec.TYPE_STRUCT);
125         nodeRow = new StructSaver(table, collection.getUOIDManager());
126         batcher.addStatement(nodeRow.getStatement(connection), table.getName(),
127         RepositoryProperties.getIntProperty(CONF_TREE_BATCHSIZE));
128         
129         // extra-data table
130
table = collection.getTableInfo(TableSpec.TYPE_EXTRA);
131         extraNodeRow = new ExtraDataSaver(table);
132         batcher.addStatement(extraNodeRow.getStatement(connection), table.getName(),
133         RepositoryProperties.getIntProperty(CONF_TREE_BATCHSIZE));
134     }
135     
136     public String JavaDoc allocateDocOID(String JavaDoc docID) throws RepositoryException
137     {
138         DID = manager.getNewDocOID();
139         UDID = manager.buildUDID(DID);
140         if (docID == null)
141             docID = Long.toString(DID);
142         this.docID = docID;
143         nodeRow.set(DID);
144         extraNodeRow.set(DID);
145         return this.docID;
146     }
147     
148     /**
149      * For PI, comments and prefix definition.
150      **/

151     public void extraNode(byte type, StoragePathMetadata path, String JavaDoc data, Locator JavaDoc locator)
152     throws XMLDBCException, SQLException JavaDoc
153     {
154         if ((data != null) && (data.length() > extraDataMaxSize))
155         {
156             String JavaDoc msg;
157             switch (type)
158             {
159                 case NodeKind.COMMENT:
160                     msg = "Comment discarded";
161                     break;
162                 case NodeKind.PI:
163                     msg = "Processing instruction discarded";
164                     break;
165                 default:
166                     msg = "Extra data discarded";
167             }
168             if (getXMLErrorHandler() != null)
169                 getXMLErrorHandler().warning(new RepositoryException(RepositoryException.EXTRA_DATA_LOSS, msg));
170         }
171         else
172         {
173             if (path.getPathID() == ROOT_PATH_ID)
174                 wExtraNode.set(manager.buildUOID(DID, 0), ++rowNum, ROOT_PATH_ID, type, ++rootPosition, 0, data);
175             else
176                 wExtraNode.set(getAnchor(), ++rowNum, path.getPathID(), type,
177                 type == NodeKind.NAMESPACE ? 0 : getCurrentModelNode().incModelPosition(),
178                 getCurrentModelNode().getCharOffset(), data);
179                 batcher.addBatch(extraNodeRow.setParameters(wExtraNode), locator.getLineNumber(), locator.getColumnNumber());
180         }
181         // update stats
182
nodeCount++;
183         if (data != null)
184             docDataSize += data.length();
185     }
186     
187     protected AbstractModelNode getLogicalStructureNode(byte type, StoragePathMetadata path, String JavaDoc data, Locator JavaDoc locator)
188     throws SQLException JavaDoc, XMLDBCException
189     {
190         List JavaDoc nodeTableMappings = null, nodeColumnMappings = null;
191         short modelPosition = 0;
192         short rank = 0;
193         
194         // process mappings for elements & attributes & increment parent child counters
195
ModelNode parent = getCurrentModelNode();
196         switch (type)
197         {
198             case NodeKind.TEXT:
199                 nodeTableMappings = textTableMappingList;
200                 modelPosition = parent.incModelPosition(); // save for model child ordering
201
break;
202                 
203             case NodeKind.ELEMENT:
204                 if (lastOID != -1)
205                     getCurrentModelNode().flushExtraNode(); // because xsi attributes adding is achieved now
206
// no need to wait endElement for non-leaf elements
207
nodeTableMappings = path.getTableMappings();
208                 nodeColumnMappings = path.getColumnMappings();
209                 if (lastOID == -1) // root element
210
rootPosition++;
211                 else
212                 {
213                     rank = parent.incChildCount();
214                     modelPosition = parent.incModelPosition(); // save for model child ordering
215
}
216                 break;
217                 
218             case NodeKind.ATTRIBUTE:
219                 nodeTableMappings = path.getTableMappings();
220                 nodeColumnMappings = path.getColumnMappings();
221                 parent.incAttCount();
222             break;
223                 
224             default :
225         }
226     
227         // allocate or recycle a node
228
ModelNode node = (ModelNode)stack.push();
229         node.set(path, type, data, rank, modelPosition, locator);
230         
231         if (path.isPersistent() || (type == NodeKind.TEXT)) // OID allocation
232
{
233             // Now that parent has a children with UOID, if POTENTIAL_STRUCT, change its path sign
234

235             if ((parent != null) && (parent.getPath().getStorageMode() == StoragePathMetadata.POTENTIAL_STRUCT))
236                 parent.schedulePathSignToggle();
237             
238             if (type == NodeKind.TEXT)
239                 node.schedulePathSignToggle();
240             
241             node.setOID(++lastOID);
242             
243             // Process table mappings (create tupleNode)
244
// the MIXED/leaf stay without column mapping (and potentially no table mapping)
245
// till we know if they are model leaf
246
if (nodeTableMappings != null)
247                 node.setTupleNode(storageBuffer.bufferizeNode(nodeTableMappings, locator));
248             
249         }
250         else if (!path.isPositionEnforced())
251             node.addPositionExtraNode();
252         
253         // Process column mappings (set dependencies)
254
if (nodeColumnMappings != null)
255         {
256             // Create dependance links between tupleNode (references : up to now we
257
// consider that ref => (FK,PK) and thus a tuple must no be stored
258
// before its master.
259
Iterator JavaDoc it = nodeColumnMappings.iterator();
260             ColumnMapping column = null;
261             int refIndex;
262             while (it.hasNext())
263             {
264                 column = (ColumnMapping)it.next();
265                 refIndex = column.getTableRefIndex();
266                 if (refIndex != -1)
267                     storageBuffer.getTupleFactory(column.getTableIndex()).addMasterTuple(storageBuffer.getTupleFactory(refIndex).getTuple());
268             }
269         }
270         
271         // update stats
272
nodeCount++;
273         
274         return node;
275     }
276     
277     protected AbstractModelNode finalizeNode(Locator JavaDoc locator)
278     throws XMLDBCException, SQLException JavaDoc
279     {
280         ModelNode node = getCurrentModelNode();
281         
282         // switch path sign to negative if detected necessary in a child: made her because context is pertinent
283
node.togglePathSignInData();
284         
285         // set last OID of the range
286
node.last = lastOID;
287         
288         // process mappings for elements & attributes
289
List JavaDoc nodeColumnMappings = null;
290         StorageBuffer.BufferNode tupleNode = node.getTupleNode();
291         PathMetadata path = (PathMetadata)node.getPath();
292         
293         switch (node.type)
294         {
295             case NodeKind.TEXT:
296                 nodeColumnMappings = textColumnMappingList;
297                 break;
298                 
299             case NodeKind.ELEMENT:
300                 // by default
301
nodeColumnMappings = node.getPath().getColumnMappings();
302                 
303                 if ((nodeColumnMappings == null) && node.isLeaf())
304                 {
305                     // add default mapping but with potential attributes
306
if ((path.getStorageMode() & POTENTIAL_DATA) != 0)
307                     {
308                         if (path.getReadColumnMapping() != null)
309                         {
310                             // set the default mapping typed)
311
nodeColumnMappings = Collections.singletonList(path.getReadColumnMapping());
312                             if (tupleNode == null)
313                                 tupleNode = storageBuffer.bufferizeDefaultNode(path.getReadTableMapping(), locator);
314                             else
315                                 tupleNode.addDefaultTuple(path.getReadTableMapping(), locator);
316                             break;
317                         }
318                         else if (path.isMixed()) // add default mapping for mixed nodes
319
{
320                             // set the default mapping (untyped)
321
nodeColumnMappings = textColumnMappingList;
322                             if (tupleNode == null)
323                                 tupleNode = storageBuffer.bufferizeDefaultNode(path.getTextTableMapping(), locator);
324                             else
325                                 tupleNode.addDefaultTuple(path.getTextTableMapping(), locator);
326                             break;
327                         }
328                     }
329                 }
330                 break;
331                 
332             case NodeKind.ATTRIBUTE:
333                 nodeColumnMappings = node.getPath().getColumnMappings();
334                 if (nodeColumnMappings == null)
335                 {
336                     nodeColumnMappings = Collections.singletonList(path.getReadColumnMapping());;
337                     tupleNode = storageBuffer.bufferizeDefaultNode(path.getReadTableMapping(), locator);
338                 }
339                 break;
340             default :
341         }
342         
343         // process column mappings (simple type elements or attributes)
344
if (nodeColumnMappings != null)
345         {
346             Iterator JavaDoc it = nodeColumnMappings.iterator();
347             ColumnMapping column = null;
348             
349             while (it.hasNext())
350             {
351                 column = (ColumnMapping)it.next();
352                 storageBuffer.getTupleFactory(column.getTableIndex()).getTuple().addColumnMappingData(column, this);
353             }
354         }
355         
356         // Complete table mappings
357
if (tupleNode != null)
358             tupleNode.complete();
359         
360         // save struct if any
361
node.flush();
362         
363         // TO IMPROVE save data : could be done on mapping complete
364
if (node.type == NodeKind.ELEMENT)
365         {
366             // set tuple to complete, only waiting for dependences
367
// TO IMPLEMENT : check before endElement that no subelement is still waited for an element completion
368
//storageBuffer.getTupleFactory(index).setCompleted();
369

370             // try to flush completed tupleNode when an element is ended
371
// TO DO : refine it : do it only when necessary
372
storageBuffer.flush();
373         }
374         if (node.isLeaf())
375             node.flushExtraNode(); // because xsi:nil attribute is sure now
376

377         // update stats (now that is in the stack
378
if (node.getData() != null)
379             docDataSize += node.getData().length();
380
381         return (ModelNode)stack.pop();
382     }
383     
384     /* return the last OID allocated */
385     public void endModel() throws XMLDBCException, SQLException JavaDoc
386     {
387         super.endModel();
388         
389         idStmt.setString(1, docID);
390         idStmt.setLong(2, UDID);
391         idStmt.setTimestamp(3, new java.sql.Timestamp JavaDoc(System.currentTimeMillis()));
392         idStmt.setLong(4, nodeCount);
393         idStmt.setInt(5, (int)(docDataSize/nodeCount));
394         batcher.addBatch(idStmt, 0, 0);
395     }
396     
397     public void reset()
398     throws RepositoryException, SQLException JavaDoc
399     {
400         super.reset();
401         lastOID = -1;
402         nodeCount = 0;
403         docDataSize = 0;
404         rootPosition = 0;
405         rowNum = 0;
406         DID = -1;
407         UDID = -1;
408         docID = null;
409         wExtraNode.clear();
410     }
411     
412     public void close() throws RepositoryException, SQLException JavaDoc
413     {
414         super.close();
415         idStmt.close();
416         idStmt = null;
417         nodeRow.close();
418         extraNodeRow.close();
419     }
420     
421     public ModelNode getCurrentModelNode()
422     {
423         return (ModelNode)stack.top();
424     }
425     
426     ////////////////////////////////////////////////////////////////////
427
// IMPLEMENTATION of StorageContext interface.
428
////////////////////////////////////////////////////////////////////
429
public long getBucketOID() { return DID;}
430     public short getPathOID()
431     {
432         ModelBuilder.ModelNode node = getCurrentModelNode();
433         if (node.negativePath())
434             return (short)-node.path;
435         else
436             return node.path;
437     }
438     public String JavaDoc getDocumentID()
439     { return docID;}
440     public long getDocumentOID()
441     { return UDID;}
442     public long getOID()
443     {return getCurrentModelNode().oid;}
444     public long getUOID()
445     { return manager.buildUOID(DID, getCurrentModelNode().oid);}
446     public RecyclingStack.StackObject newStackObject()
447     {
448         return new ModelNode();
449     }
450     
451     //
452
// PRIVATE UTILITIES
453
//
454

455     private long getAnchor()
456     {
457         ModelNode wNode;
458         for (int i = stack.size() - 1; i >= 0 ; i--)
459         {
460             wNode = (ModelNode)stack.get(i);
461             if (wNode.oid >= 0)
462                 return manager.buildUOID(DID, wNode.oid);
463         }
464         return -1;
465     }
466     
467     ////////////////////////////////////////////////////////////////////
468
// STORAGE MODEL NODE
469
////////////////////////////////////////////////////////////////////
470
public class ModelNode extends AbstractModelNode
471     implements RecyclingStack.StackObjectFactory
472     {
473         private static final String JavaDoc RCSRevision = "$Revision: 1.1 $";
474         private static final String JavaDoc RCSName = "$Name: $";
475         private int charOffset =0;
476         private RecyclingStack extraNodes = null; // for positionning or nil
477
protected short attCount = 0; // attributes count
478
private short extraDataCount = 0; // number of extra-nodes (PI & comments + elements) in containing element
479
private short modelPosition = 0;
480         private boolean negativePath = false;
481         private boolean scheduleToggle = false;
482         int line = -1;
483         int column = -1;
484        
485         ModelNode()
486         {}
487         
488         void set(StoragePathMetadata path, byte type, String JavaDoc data, short rank, short modelPosition, Locator JavaDoc locator)
489         {
490             set(path, type, data, rank);
491             setModelPosition(modelPosition);
492             setLocation(locator);
493         }
494         
495         public void clear()
496         {
497             super.clear();
498             if (extraNodes != null)
499                 extraNodes.clear();
500             charOffset = 0;
501             attCount = 0;
502             extraDataCount = 0;
503             modelPosition = 0;
504             negativePath = false;
505             scheduleToggle = false;
506             line = -1;
507             column = -1;
508         }
509
510         public void setLocation(Locator JavaDoc locator)
511         {
512             if (locator != null)
513             {
514                 line = locator.getLineNumber();
515                 column = locator.getColumnNumber();
516             }
517         }
518         
519         void setModelPosition(short pos)
520         {
521             modelPosition = pos;
522         }
523
524         boolean negativePath()
525         {
526             return negativePath;
527         }
528
529         void schedulePathSignToggle()
530         {
531             negativePath = true;
532             if (tupleNode != null) // if tuple has not been initialized yet, no need to toggle
533
scheduleToggle = true;
534         }
535         
536         void togglePathSignInData()
537         {
538             if (scheduleToggle && (tupleNode != null))
539                 tupleNode.changePathSign();
540         }
541         
542         void addPositionExtraNode()
543         {
544             if (extraNodes == null)
545                 extraNodes = new RecyclingStack(this);
546             ExtraNode extra = (ExtraNode)extraNodes.push();
547             extra.set(getAnchor(), ++rowNum, pathMetadata.getPathID(), type, modelPosition);
548         }
549         void flushExtraNode() throws SQLException JavaDoc, RepositoryException
550         {
551             if (extraNodes != null)
552             {
553                ExtraNode extra;
554                while((extra = (ExtraNode)extraNodes.pop()) != null)
555                {
556                    batcher.addBatch(extraNodeRow.setParameters(extra), line, column);
557                }
558            }
559         }
560         void flush() throws SQLException JavaDoc, RepositoryException
561         {
562             if ((getNodeType() != NodeKind.TEXT) && ((pathMetadata.getStorageMode() & NO_STRUCT) == 0)
563             && !((pathMetadata.getStorageMode() == StoragePathMetadata.POTENTIAL_STRUCT) && isEmptyRange()))
564                  batcher.addBatch(nodeRow.setParameters(this), line, column);
565         }
566
567         short incAttCount()
568         {
569             return ++attCount;
570         }
571         
572         boolean isModelLeaf()
573         {
574             return ((childCount + attCount) == 0);
575         }
576
577         boolean isEmptyRange()
578         {
579             return (oid == last);
580         }
581
582         void setOID(long oid)
583         {
584             this.oid = oid;
585         }
586         
587         public boolean setXSIinfo(String JavaDoc attName, String JavaDoc value)
588         throws RepositoryException
589         {
590             boolean nil = super.setXSIinfo(attName, value);
591             if (nil && pathMetadata.isNilImplicit()) // no extra node needed
592
return nil;
593             
594             ExtraNode xsiNode;
595             if ((extraNodes == null) || (extraNodes.size() == 0))
596                 addPositionExtraNode();
597             xsiNode = (ExtraNode)extraNodes.get(0);
598             if (xsiNode.type >= RepositoryConstants.XSI_NIL) // position node cannot be reused
599
{
600                 xsiNode = (ExtraNode)extraNodes.push();
601                 xsiNode.set(lastOID, pathMetadata.getPathID(), type, modelPosition);
602             }
603             // use schema to convert value
604
ElementPSVInfoset infoSet = validationContext.getCurrentInfoset();
605             PSVInfoset attInfoSet = infoSet.getAttributePSVInfoset(SchemaConstants.XSI_URI, attName);
606             // set type & data
607
if (nil)
608             {
609                 xsiNode.type = RepositoryConstants.XSI_NIL;
610                 xsiNode.data = attInfoSet.getNormalizedValue().toString();
611             }
612             else if (attName.equals(SchemaConstants.XSI_TYPE_ATTR))
613             {
614                 if ((pathMetadata.getReferenceColumnMapping() != null)
615                 || (
616                     (pathMetadata.getTableMappings() != null)
617                     && (pathMetadata.getTableMappings().size() > 0)
618                     ))
619                     throw new RepositoryException(RepositoryException.NOT_ALLOWED,
620                     "Dynamic XML schema type binding (xsi:type) is not supported for mapped elements or attributes.");
621                 xsiNode.type = RepositoryConstants.XSI_TYPE;
622                 xsiNode.data = attInfoSet.getActualValue().toString();
623             }
624             else if (attName.equals(SchemaConstants.XSI_SCHEMA_LOCATION_ATTR))
625             {
626                 xsiNode.type = RepositoryConstants.XSI_SCHEMA_LOCATION;
627                 xsiNode.data = attInfoSet.getNormalizedValue().toString();
628             }
629             else if (attName.equals(SchemaConstants.XSI_NO_NAMESPACE_SCHEMA_LOCATION_ATTR))
630             {
631                 xsiNode.type = RepositoryConstants.XSI_NO_NAMESPACE_SCHEMA_LOCATION;
632                 xsiNode.data = attInfoSet.getNormalizedValue().toString();
633             }
634             
635 // xsiNode.data = value;
636
return nil;
637         }
638
639         public void incCharOffset(int length)
640         {
641             charOffset += length;
642         }
643         
644         public short incModelPosition()
645         {
646             return ++extraDataCount;
647         }
648         
649         int getCharOffset()
650         {
651             return charOffset;
652         }
653         
654         public RecyclingStack.StackObject newStackObject()
655         {
656             return new ExtraNode();
657         }
658     }
659     
660 }
661
Popular Tags