001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.module.core;
011:
012: import java.util.*;
013:
014: import org.mmbase.core.util.StorageConnector;
015: import org.mmbase.storage.*;
016: import org.mmbase.storage.search.*;
017: import org.mmbase.storage.search.implementation.*;
018: import org.mmbase.util.functions.FunctionProvider;
019: import org.mmbase.util.logging.Logger;
020: import org.mmbase.util.logging.Logging;
021:
022: /**
023: * MMTable is the base abstraction of a cloud of objects stored in one database table,
024: * essentially a cloud of objects of the same type.
025: * It provides a starting point for MMObjectBuilder by defining a scope - the database table -
026: * and basic functionality to create the table and query properties such as its size.
027: * This class does not contain actual management of nodes (this is left to MMOBjectBuilder).
028: *
029: * @author Daniel Ockeloen
030: * @author Pierre van Rooden (javadoc)
031: * @version $Id: MMTable.java,v 1.27 2007/02/11 14:46:13 nklasens Exp $
032: */
033: public abstract class MMTable extends FunctionProvider {
034:
035: private static final Logger log = Logging
036: .getLoggerInstance(MMTable.class);
037:
038: /**
039: * The MMBase module that this table belongs to
040: */
041: protected MMBase mmb;
042:
043: /**
044: * The table name
045: */
046: protected String tableName;
047:
048: /**
049: * Maximum number of nodes to return on a query (-1 means no limit, and is also the default)
050: */
051: protected int maxNodesFromQuery = -1;
052:
053: // link to the storage layer
054: protected StorageConnector storageConnector;
055:
056: /**
057: * Empty constructor.
058: */
059: public MMTable() {
060: }
061:
062: /**
063: * Set the MMBase object, and retrieve the storage layer.
064: * @param m the MMBase object to set as owner of this builder
065: */
066: public void setMMBase(MMBase m) {
067: mmb = m;
068: }
069:
070: /**
071: * Return the MMBase object
072: * @since 1.7
073: */
074: public MMBase getMMBase() {
075: return mmb;
076: }
077:
078: /**
079: * Set tablename of the builder. Should be used to initialize a MMTable object before calling init().
080: * @param tableName the name of the table
081: */
082: public void setTableName(String tableName) {
083: this .tableName = tableName;
084: }
085:
086: /**
087: * Retrieve the table name (without the clouds' base name)
088: * @return a <code>String</code> containing the table name
089: * @since MMBase-1.7
090: */
091: public String getTableName() {
092: return tableName;
093: }
094:
095: /**
096: * Retrieve the full table name (including the clouds' base name)
097: * @return a <code>String</code> containing the full table name
098: * @since MMBase-1.7
099: */
100: public String getFullTableName() {
101: return mmb.baseName + "_" + tableName;
102: }
103:
104: /**
105: * Determine the number of objects in this table.
106: * @return The number of entries in the table.
107: */
108: public int size() {
109: return storageConnector.size();
110: }
111:
112: /**
113: * Check whether the table is accessible.
114: * In general, this means the table does not exist. Please note that this routine may
115: * also return false if the table is inaccessible due to insufficient rights.
116: * @return <code>true</code> if the table is accessible, <code>false</code> otherwise.
117: */
118: public boolean created() {
119: return storageConnector.created();
120: }
121:
122: public StorageConnector getStorageConnector() {
123: return storageConnector;
124: }
125:
126: /*
127: public Map getIndices() {
128: return storageConnector.getIndices();
129: }
130:
131: public void addIndex(Index index) {
132: storageConnector.addIndex(index);
133: }
134:
135: public void addIndices(List indexList) {
136: storageConnector.addIndices(indexList);
137: }
138:
139: public Index getIndex(String key) {
140: return storageConnector.getIndex(key);
141: }
142:
143: public Index createIndex(String key) {
144: return storageConnector.createIndex(key);
145: }
146:
147: public void addToIndex(String key, Field field) {
148: createIndex(key).add(field);
149: }
150:
151: public void removeFromIndex(String key, Field field) {
152: storageConnector.removeFromIndex(key, field);
153: }
154:
155: public boolean isInIndex(String key, Field field) {
156: storageConnector.isInIndex(key, field);
157: }
158: */
159:
160: // retrieve nodes
161: /**
162: * Retrieves a node based on it's number (a unique key).
163: * @todo when something goes wrong, the method currently catches the exception and returns null.
164: * It should actually throw a NotFoundException instead.
165: * @param number The number of the node to search for
166: * @param useCache If true, the node is retrieved from the node cache if possible.
167: * @return <code>null</code> if the node does not exist, the key is invalid,or a
168: * <code>MMObjectNode</code> containing the contents of the requested node.
169: */
170: public MMObjectNode getNode(final int number, boolean useCache) {
171: try {
172: return storageConnector.getNode(number, useCache);
173: } catch (IllegalArgumentException iae) {
174: log.warn(iae.getMessage());
175: if (log.isDebugEnabled()) {
176: log.debug(iae);
177: }
178: return null;
179: } catch (StorageNotFoundException se) {
180: return null;
181: } catch (StorageException se) {
182: log.error(se.getMessage(), se);
183: return null;
184: }
185: }
186:
187: /**
188: * Retrieves an object's type. If necessary, the type is added to the cache.
189: * @todo when something goes wrong, the method currently catches the exception and returns -1.
190: * It should actually throw a NotFoundException instead.
191: * @param number The number of the node to search for
192: * @return an <code>int</code> value which is the object type (otype) of the node.
193: */
194: public int getNodeType(int number) {
195: try {
196: return storageConnector.getNodeType(number);
197: } catch (StorageException se) {
198: log.error(Logging.stackTrace(se));
199: return -1;
200: }
201: }
202:
203: // Search and query methods on a table
204:
205: /**
206: * Convert virtual nodes to real nodes based on their otype
207: *
208: * Normally a multirelations-search will return virtual nodes. These nodes
209: * will only contain values which where specified in the field-vector.
210: * This method will make real nodes of those virtual nodes.
211: *
212: * @param virtuals containing virtual nodes
213: * @return List containing real nodes, directly from this Builders
214: * @since MMBase-1.6.2
215: */
216: protected List<MMObjectNode> getNodes(
217: Collection<MMObjectNode> virtuals) {
218: List<MMObjectNode> result;
219: try {
220: result = storageConnector.getNodes(virtuals);
221: } catch (SearchQueryException sqe) {
222: log.error(sqe.getMessage() + Logging.stackTrace(sqe));
223: result = new ArrayList<MMObjectNode>();
224: }
225: return result;
226: }
227:
228: /**
229: * Counts number of nodes matching a specified constraint.
230: *
231: * @param where The constraint, can be a SQL where-clause, a MMNODE
232: * expression or an altavista-formatted expression.
233: * @return The number of nodes, or -1 when failing to retrieve the data.
234: * @deprecated Use {@link #count(NodeSearchQuery) count(NodeSearchQuery)}
235: * instead.
236: */
237: public int count(String where) {
238: // In order to support this method:
239: // - Exceptions of type SearchQueryExceptions are caught.
240: int result = -1;
241: NodeSearchQuery query = storageConnector.getSearchQuery(where);
242: try {
243: result = count(query);
244: } catch (SearchQueryException e) {
245: log.error(e);
246: }
247: return result;
248: }
249:
250: /**
251: * Counts number of nodes matching a specified constraint.
252: * The constraint is specified by a query that selects nodes of
253: * a specified type, which must be the nodetype corresponding
254: * to this builder.
255: *
256: * @param query The query.
257: * @return The number of nodes.
258: * @throws IllegalArgumentException when an invalid argument is supplied.
259: * @throws SearchQueryException when failing to retrieve the data.
260: * @since MMBase-1.7
261: */
262: public int count(NodeSearchQuery query) throws SearchQueryException {
263: return storageConnector.count(query);
264: }
265:
266: /**
267: * Returns nodes matching a specified constraint.
268: * The constraint is specified by a query that selects nodes of
269: * a specified type, which must be the nodetype corresponding
270: * to this builder.
271: *
272: * @param query The query.
273: * @return The nodes.
274: * @throws IllegalArgumentException When the nodetype specified
275: * by the query is not the nodetype corresponding to this builder.
276: * @since MMBase-1.7
277: */
278: public List<MMObjectNode> getNodes(NodeSearchQuery query)
279: throws SearchQueryException {
280: return storageConnector.getNodes(query);
281: }
282:
283: // most of those below are deprecated
284:
285: /**
286: * Build a set command string from a set nodes
287: * @param nodes Vector containg the nodes to put in the set
288: * @param fieldName fieldname whose values should be put in the set
289: * @return a comma-seperated list of values, as a <code>String</code>
290: * @deprecated should be moved?
291: */
292: /*
293: public String buildSet(Vector nodes, String fieldName) {
294: StringBuffer result = new StringBuffer("(");
295: Enumeration enumeration = nodes.elements();
296: MMObjectNode node;
297:
298: while (enumeration.hasMoreElements()) {
299: node = (MMObjectNode)enumeration.nextElement();
300:
301: if(enumeration.hasMoreElements()) {
302: result.append(node.getValue(fieldName)).append(", ");
303: } else {
304: result.append(node.getValue(fieldName));
305: }
306:
307: }
308: result.append(')');
309: return result.toString();
310: }
311: */
312:
313: /**
314: * Enumerate all the objects that match the searchkeys
315: * @param where scan expression that the objects need to fulfill
316: * @return an <code>Enumeration</code> containing all the objects that apply.
317: * @deprecated Use {@link #getNodes(NodeSearchQuery)
318: * getNodes(NodeSearchQuery} to perform a node search.
319: */
320: public Enumeration<MMObjectNode> search(String where) {
321: return searchVector(where).elements();
322: }
323:
324: /**
325: * Enumerate all the objects that match the searchkeys
326: * @param where where clause that the objects need to fulfill
327: * @param sort order in which to return the objects
328: * @return an <code>Enumeration</code> containing all the objects that apply.
329: * @deprecated Use {@link #getNodes(NodeSearchQuery)
330: * getNodes(NodeSearchQuery} to perform a node search.
331: */
332: /*
333: public Enumeration search(String where, String sort) {
334: return searchVector(where, sort).elements();
335: }
336: */
337:
338: /**
339: * Enumerate all the objects that match the searchkeys
340: * @param where where clause that the objects need to fulfill
341: * @param sort order in which to return the objects
342: * @param direction sorts ascending if <code>true</code>, descending if <code>false</code>.
343: * Only applies if a sorted order is given.
344: * @return an <code>Enumeration</code> containing all the objects that apply.
345: * @deprecated Use {@link #getNodes(NodeSearchQuery)
346: * getNodes(NodeSearchQuery} to perform a node search.
347: */
348: /*
349: public Enumeration search(String where, String sort, boolean direction) {
350: return searchVector(where, sort, direction).elements();
351: }
352: */
353:
354: /**
355: * Returns a vector containing all the objects that match the searchkeys
356: * @param where scan expression that the objects need to fulfill
357: * @return a vector containing all the objects that apply.
358: * @deprecated Use {@link #getNodes(NodeSearchQuery)
359: * getNodes(NodeSearchQuery} to perform a node search.
360: */
361: public Vector<MMObjectNode> searchVector(String where) {
362: // In order to support this method:
363: // - Exceptions of type SearchQueryExceptions are caught.
364: // - The result is converted to a vector.
365: Vector<MMObjectNode> result = new Vector<MMObjectNode>();
366: NodeSearchQuery query = storageConnector.getSearchQuery(where);
367: try {
368: List<MMObjectNode> nodes = getNodes(query);
369: result.addAll(nodes);
370: } catch (SearchQueryException e) {
371: log.error(e);
372: }
373: return result;
374: }
375:
376: /**
377: * Returns all the nodes from the builder.
378: * @return The nodes.
379: */
380: public List<MMObjectNode> getNodes() {
381: try {
382: List<MMObjectNode> nodes = storageConnector.getNodes();
383: if (nodes != null) {
384: return nodes;
385: }
386: } catch (SearchQueryException e) {
387: log.error(e);
388: }
389: return new ArrayList<MMObjectNode>();
390: }
391:
392: /**
393: * Returns a vector containing all the objects that match the searchkeys
394: * @param where where clause that the objects need to fulfill
395: * @param sorted a comma separated list of field names on wich the
396: * returned list should be sorted
397: * @return a vector containing all the objects that apply.
398: * @deprecated Use {@link #getNodes(NodeSearchQuery)
399: * getNodes(NodeSearchQuery} to perform a node search.
400: */
401: /*
402: public Vector searchVector(String where, String sorted) {
403: return searchVector(where, sorted, true);
404: }
405: */
406:
407: /**
408: * Returns a vector containing all the objects that match the searchkeys
409: * @param where where clause that the objects need to fulfill
410: * @param sorted order in which to return the objects
411: * @param direction sorts ascending if <code>true</code>, descending if <code>false</code>.
412: * Only applies if a sorted order is given.
413: * @return a vector containing all the objects that apply.
414: * @deprecated Use {@link #getNodes(NodeSearchQuery)
415: * getNodes(NodeSearchQuery} to perform a node search.
416: */
417: /*
418: public Vector searchVector(String where, String sorted, boolean direction) {
419: String directions = (direction? "UP": "DOWN");
420: return searchVector(where, sorted, directions);
421: }
422: */
423:
424: /**
425: * Returns a vector containing all the objects that match the searchkeys in
426: * a given order.
427: *
428: * @param where where clause that the objects need to fulfill
429: * @param sorted a comma separated list of field names on wich the
430: * returned list should be sorted
431: * @param directions A comma separated list of the values indicating wether
432: * to sort up (ascending) or down (descending) on the
433: * corresponding field in the <code>sorted</code>
434: * parameter or <code>null</code> if sorting on all
435: * fields should be up.
436: * The value DOWN (case insensitive) indicates
437: * that sorting on the corresponding field should be
438: * down, all other values (including the
439: * empty value) indicate that sorting on the
440: * corresponding field should be up.
441: * If the number of values found in this parameter are
442: * less than the number of fields in the
443: * <code>sorted</code> parameter, all fields that
444: * don't have a corresponding direction value are
445: * sorted according to the last specified direction
446: * value.
447: * @return a vector containing all the objects that apply in the
448: * requested order
449: * @deprecated Use {@link #getNodes(NodeSearchQuery)
450: * getNodes(NodeSearchQuery} to perform a node search.
451: */
452: /*
453: public Vector searchVector(String where, String sorted, String directions) {
454: // In order to support this method:
455: // - Exceptions of type SearchQueryExceptions are caught.
456: // - The result is converted to a vector.
457: Vector result = new Vector();
458: NodeSearchQuery query = getSearchQuery(where, sorted, directions);
459: try {
460: List nodes = getNodes(query);
461: result.addAll(nodes);
462: } catch (SearchQueryException e) {
463: log.error(e);
464: }
465: return result;
466: }
467: */
468:
469: /**
470: * As searchVector. Differences are:
471: * - Throws exception on SQL errors
472: * @since MMBase-1.6
473: * @deprecated Use {@link #getNodes(NodeSearchQuery)
474: * getNodes(NodeSearchQuery} to perform a node search.
475: */
476: /*
477: public List searchList(String where) {
478: // In order to support this method:
479: // - Exceptions of type SearchQueryExceptions are wrapped
480: // inside an RuntimeException.
481: NodeSearchQuery query = getSearchQuery(where);
482: try {
483: return getNodes(query);
484: } catch (SearchQueryException e) {
485: throw new RuntimeException(e);
486: }
487: }
488: */
489:
490: /**
491: * As searchVector
492: * But
493: * - throws Exception on error
494: * - returns List
495: *
496: * @param where Constraint, represented by scan MMNODE expression,
497: * AltaVista format or SQL "where"-clause.
498: * @param sorted Comma-separated list of names of fields to sort on.
499: * @param directions Comma-separated list of sorting directions ("UP"
500: * or "DOWN") of the fields to sort on.
501: * @since MMBase-1.6
502: * @deprecated Use {@link #getNodes(NodeSearchQuery)
503: * getNodes(NodeSearchQuery} to perform a node search.
504: */
505: /*
506: public List searchList(String where, String sorted, String directions) {
507: // In order to support this method:
508: // - Exceptions of type SearchQueryExceptions are wrapped
509: // inside an RuntimeException.
510: NodeSearchQuery query = getSearchQuery(where, sorted, directions);
511: try {
512: return getNodes(query);
513: } catch (SearchQueryException e) {
514: if (log.isDebugEnabled()) {
515: log.debug(e + Logging.stackTrace(e));
516: }
517: throw new RuntimeException(e);
518: }
519: }
520: */
521:
522: /**
523: * Creates search query that retrieves nodes matching a specified
524: * constraint.
525: *
526: * @param where The constraint, can be a SQL where-clause, a MMNODE
527: * expression, an altavista-formatted expression, empty or
528: * <code>null</code>.
529: * @return The query.
530: * @since MMBase-1.7
531: */
532: /*
533: NodeSearchQuery getSearchQuery(String where) {
534: NodeSearchQuery query;
535:
536: if (where != null && where.startsWith("MMNODE ")) {
537: // MMNODE expression.
538: query = storageConnector.convertMMNodeSearch2Query(where);
539: } else {
540: query = new NodeSearchQuery((MMObjectBuilder)this);
541: QueryConvertor.setConstraint(query, where);
542: }
543:
544: return query;
545: }
546: */
547:
548: /**
549: * Creates search query that retrieves a sorted list of nodes,
550: * matching a specified constraint.
551: *
552: * @param where The constraint, can be a SQL where-clause, a MMNODE
553: * expression or an altavista-formatted expression.
554: * @param sorted Comma-separated list of names of fields to sort on.
555: * @param directions Comma-separated list of sorting directions ("UP"
556: * or "DOWN") of the fields to sort on.
557: * If the number of sorting directions is less than the number of
558: * fields to sort on, the last specified direction is applied to
559: * the remaining fields.
560: * @since MMBase-1.7
561: */
562: /*
563: NodeSearchQuery getSearchQuery(String where, String sorted, String directions) {
564: NodeSearchQuery query = getSearchQuery(where);
565: if (directions == null) {
566: directions = "";
567: }
568: StringTokenizer sortedTokenizer = new StringTokenizer(sorted, ",");
569: StringTokenizer directionsTokenizer = new StringTokenizer(directions, ",");
570:
571: String direction = "UP";
572: while (sortedTokenizer.hasMoreElements()) {
573: String fieldName = sortedTokenizer.nextToken().trim();
574: CoreField coreField = getField(fieldName);
575: if (coreField == null) {
576: throw new IllegalArgumentException(
577: "Not a known field of builder " + getTableName()
578: + ": '" + fieldName + "'");
579: }
580: StepField field = query.getField(coreField);
581: BasicSortOrder sortOrder = query.addSortOrder(field);
582: if (directionsTokenizer.hasMoreElements()) {
583: direction = directionsTokenizer.nextToken().trim();
584: }
585: if (direction.equalsIgnoreCase("DOWN")) {
586: sortOrder.setDirection(SortOrder.ORDER_DESCENDING);
587: } else {
588: sortOrder.setDirection(SortOrder.ORDER_ASCENDING);
589: }
590: }
591: return query;
592: }
593: */
594:
595: /**
596: * Adds nodenumbers to be included to query retrieving nodes.
597: *
598: * @param query The query.
599: * @param nodeNumbers Comma-separated list of nodenumbers.
600: * @since MMBase-1.7
601: */
602: /*
603: void addNodesToQuery(NodeSearchQuery query, String nodeNumbers) {
604: BasicStep step = (BasicStep) query.getSteps().get(0);
605: StringTokenizer st = new StringTokenizer(nodeNumbers, ",");
606: while (st.hasMoreTokens()) {
607: String str = st.nextToken().trim();
608: int nodeNumber = Integer.parseInt(str);
609: step.addNode(nodeNumber);
610: }
611: }
612: */
613:
614: }
|