001: /*
002: * $Header: /export/home/cvsroot/MyPersonalizerRepository/MyPersonalizer/Subsystems/Kernel/Sources/es/udc/mypersonalizer/kernel/model/repository/sql/storers/FindMetaPropertyVisitor.java,v 1.1.1.1 2004/03/25 12:08:36 fbellas Exp $
003: * $Revision: 1.1.1.1 $
004: * $Date: 2004/03/25 12:08:36 $
005: *
006: * =============================================================================
007: *
008: * Copyright (c) 2003, The MyPersonalizer Development Group
009: * (http://www.tic.udc.es/~fbellas/mypersonalizer/index.html) at
010: * University Of A Coruna
011: * All rights reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions are met:
015: *
016: * - Redistributions of source code must retain the above copyright notice,
017: * this list of conditions and the following disclaimer.
018: *
019: * - Redistributions in binary form must reproduce the above copyright notice,
020: * this list of conditions and the following disclaimer in the documentation
021: * and/or other materials provided with the distribution.
022: *
023: * - Neither the name of the University Of A Coruna nor the names of its
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
028: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
029: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
030: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
031: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
032: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
033: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
034: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
035: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
036: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
037: * POSSIBILITY OF SUCH DAMAGE.
038: *
039: */
040:
041: package es.udc.mypersonalizer.kernel.model.repository.sql.storers;
042:
043: import java.sql.PreparedStatement;
044: import java.sql.SQLException;
045: import java.sql.ResultSet;
046: import java.sql.Connection;
047: import java.util.Map;
048: import java.util.HashMap;
049: import java.util.Iterator;
050: import java.util.ArrayList;
051:
052: import es.udc.mypersonalizer.kernel.log.Log;
053: import es.udc.mypersonalizer.kernel.log.LogManager;
054: import es.udc.mypersonalizer.kernel.log.LogNamingConventions;
055: import es.udc.mypersonalizer.kernel.util.exceptions.VisitorException;
056: import es.udc.mypersonalizer.kernel.model.properties.Property;
057: import es.udc.mypersonalizer.kernel.model.properties.SimpleProperty;
058: import es.udc.mypersonalizer.kernel.model.properties.CompoundProperty;
059: import es.udc.mypersonalizer.kernel.model.properties.PropertyStructure;
060: import es.udc.mypersonalizer.kernel.model.properties.IllegalStringValuesException;
061: import es.udc.mypersonalizer.kernel.model.properties.PropertyEditorNotFoundException;
062: import es.udc.mypersonalizer.kernel.model.annotators.sql.SQLPersistenceTypeAnnotationHelper;
063: import es.udc.mypersonalizer.kernel.model.metainfo.MetaProperty;
064: import es.udc.mypersonalizer.kernel.model.metainfo.MetaSimpleProperty;
065: import es.udc.mypersonalizer.kernel.model.metainfo.MetaCompoundProperty;
066: import es.udc.mypersonalizer.kernel.model.repository.sql.config.DatabaseConventionsConfig;
067: import es.udc.mypersonalizer.kernel.model.repository.sql.config.DatabaseConventionsConfigManager;
068:
069: /**
070: * This class is a concrete Visitor in the "Visitor" pattern.
071: * It defines a way to <b>retrieve</b> a <code>Property</code>
072: * from a database by means of a object to relational mapping.
073: * Each property is kept in one or more database tables.
074: * <p>
075: * It is responsability of the "Visitor" implementations to
076: * make the traverse through the hierarchical structure.
077: *
078: * @author Abel Muinho
079: * @author Fernando Bellas
080: * @since 1.0
081: */
082:
083: public class FindMetaPropertyVisitor extends
084: AbstractSQLMetaPropertyVisitor {
085:
086: private static final String GENERATED_IDENTIFIER_COLUMN_NAME;
087: private static final String PROPERTY_IDENTIFIER_COLUMN_NAME;
088:
089: static {
090: String generatedIdentifierColumnName = null;
091: String propertyIdentifierColumnName = null;
092: try {
093: DatabaseConventionsConfig config = DatabaseConventionsConfigManager
094: .getConfig();
095: generatedIdentifierColumnName = config
096: .getGeneratedIdentifierColumn();
097: propertyIdentifierColumnName = config
098: .getPropertyIdentifierColumn();
099: } catch (Exception e) {
100: Log mypersonalizerLog = LogManager
101: .getLog(LogNamingConventions.MYPERSONALIZER);
102: mypersonalizerLog.write(
103: "Could not initialize configuration for "
104: + "FindMetaPropertyVisitor", e,
105: FindMetaPropertyVisitor.class);
106: }
107: GENERATED_IDENTIFIER_COLUMN_NAME = generatedIdentifierColumnName;
108: PROPERTY_IDENTIFIER_COLUMN_NAME = propertyIdentifierColumnName;
109: }
110:
111: /**
112: * The <code>ResultSet</code> for this visitor.
113: */
114: private ResultSet resultSet = null;
115:
116: /**
117: * The debug mode. This code should be commented out for the final
118: * release !
119: */
120: private boolean debugMode = true;
121:
122: /**
123: * Creates an instance of this class.
124: *
125: * @param connection a connection to the database
126: * @param resultSet the <code>ResultSet</code> that contains the property.
127: * The <code>ResultSet</code> could consist of several rows. The
128: * property returned will contain all the values for all the rows
129: * in the <code>ResultSet</code>.
130: * @param key the map that contains pairs of key fields and their
131: * corresponding values. This key are related to the row or rows
132: * in the previosly explained result set.
133: */
134: public FindMetaPropertyVisitor(Connection connection,
135: ResultSet resultSet, Map key) {
136:
137: super (connection, key);
138: this .resultSet = resultSet;
139: }
140:
141: /**
142: * Visit operation for a <code>MetaSimpleProperty</code>.
143: *
144: * @param metaProperty the <code>MetaSimpleProperty</code>
145: * object over which the visitor can operate.
146: *
147: * @return an <code>Object</code> of needful for the caller.
148: * In this case it's a <code>SimpleProperty</code.>
149: * This is specially interesting for traverse of "Composite"
150: * structures. May be a <code>null</code> value
151: * @throws VisitorException if an internal error occurs while
152: * processing the visited object
153: */
154: public Object visitMetaSimpleProperty(
155: MetaSimpleProperty metaProperty) throws VisitorException {
156:
157: String[] values = null;
158:
159: /* If multi-valued ... */
160: if (metaProperty.isMultiValued()) {
161: PreparedStatement statement = null;
162: ResultSet myResultSet = null;
163: /* Select from a new table ordered by the generated id. */
164: String query = "SELECT "
165: + GENERATED_IDENTIFIER_COLUMN_NAME
166: + ", "
167: + SQLPersistenceTypeAnnotationHelper
168: .getColumnNameAnnotation(metaProperty)
169: + " FROM " + getTableName(metaProperty) + " WHERE "
170: + allAndKeys() + " ORDER BY "
171: + GENERATED_IDENTIFIER_COLUMN_NAME;
172:
173: debug("query: " + query);
174:
175: statement = createStatement(query);
176: fillKeyValues(statement, 1);
177: myResultSet = executeQuery(statement);
178: values = getValues(metaProperty, myResultSet);
179: closeStatement(statement);
180:
181: } else { // Otherwise, if single-valued...
182: /* retrieve the only value from the current result set */
183: values = getValues(metaProperty, resultSet);
184: }
185:
186: /* And return a simple property with the value(s) */
187: return buildSimpleProperty(metaProperty, values);
188: }
189:
190: /**
191: * Visit operation for a <code>MetaCompoundProperty</code>.
192: *
193: * @param metaProperty the <code>MetaCompoundProperty</code>
194: * object over which the visitor can operate.
195: *
196: * @return an <code>Object</code> of needful for the caller.
197: * In this case it's a <code>CompoundProperty</code.>
198: * This is specially interesting for traverse of "Composite"
199: * structures. May be a <code>null</code> value
200: * @throws VisitorException if an internal error occurs while
201: * processing the visited object
202: */
203: public Object visitMetaCompoundProperty(
204: MetaCompoundProperty metaProperty) throws VisitorException {
205:
206: ResultSet rs = null;
207: PreparedStatement statement = null;
208: PropertyStructure[] propertyStructures = null;
209: /* Select the values from the "tableName" table */
210: String query = "SELECT * FROM " + getTableName(metaProperty)
211: + " WHERE " + allAndKeys();
212:
213: if (metaProperty.isMultiValued()) {
214: query += " ORDER BY " + GENERATED_IDENTIFIER_COLUMN_NAME;
215: }
216:
217: debug("query: " + query);
218:
219: statement = createStatement(query);
220: fillKeyValues(statement, 1);
221: rs = executeQuery(statement);
222: propertyStructures = getValues(metaProperty, rs);
223: /* Release resources if applicable */
224: closeStatement(statement);
225:
226: /*
227: * And returns a compound property with a list of values
228: * (property structures) as an array
229: */
230: return new CompoundProperty(metaProperty.getSimpleName(),
231: propertyStructures);
232: }
233:
234: /**
235: * Creates a <code>SimpleProperty</code> given the <code>MetaProperty</code>
236: * describing it and it's values as Strings.
237: * @param metaProperty The <code>SimpleMetaProperty</code> describing the
238: * structure of the <code>Property</code> to be generated.
239: * @param values String representations of the values to be assigned to the
240: * <code>SimpleProperty</code>.
241: * @return The <code>SimpleProperty</code> created.
242: * @throws VisitorException if the <code>SimpleProperty</code> can't be
243: * built.
244: */
245: protected SimpleProperty buildSimpleProperty(
246: MetaSimpleProperty metaProperty, String[] values)
247: throws VisitorException {
248:
249: try {
250: return new SimpleProperty(metaProperty.getSimpleName(),
251: values, metaProperty.getJavaType());
252: } catch (PropertyEditorNotFoundException e) {
253: throw new VisitorException("FATAL ERROR:"
254: + " Property editor not found,"
255: + " cannot create SimpleProperty: "
256: + metaProperty.getSimpleName()
257: + " from column name: "
258: + SQLPersistenceTypeAnnotationHelper
259: .getColumnNameAnnotation(metaProperty)
260: + " with values: " + values, e);
261: } catch (IllegalStringValuesException e) {
262: throw new VisitorException("FATAL ERROR:"
263: + " Illegal string values,"
264: + " cannot create SimpleProperty: "
265: + metaProperty.getSimpleName()
266: + " from column name: "
267: + SQLPersistenceTypeAnnotationHelper
268: .getColumnNameAnnotation(metaProperty)
269: + " with values: " + values, e);
270: }
271: }
272:
273: /**
274: * Creates an array of <code>PropertyStructure</code>s representing the
275: * contents of the result set.
276: * @param metaProperty The <code>MetaCompoundProperty</code>.
277: * @param rs The <code>ResultSet</code> containing the data.
278: * @return An array of <code>PropertyStructures</code> representing the
279: * contents of the given <code>ResultSet</code>.
280: * @throws VisitorException if an error occurs while accessing the data.
281: */
282: protected PropertyStructure[] getValues(
283: MetaCompoundProperty metaProperty, ResultSet rs)
284: throws VisitorException {
285:
286: /* List of values of the compound property. */
287: ArrayList values = new ArrayList();
288:
289: try {
290: /* If there are no rows, generate a PropertyStructure[0]. */
291: /* Iterate until there are no more rows */
292: while (rs.next()) {
293: if (metaProperty.isMultiValued()) {
294: Object genId = rs
295: .getObject(GENERATED_IDENTIFIER_COLUMN_NAME);
296: /* Check if there is a value. The convention is to set
297: * genId to NULL on multivalued properties with 0 values. */
298: if (genId == null) {
299: break;
300: }
301: }
302: PropertyStructure propertyStructure = null;
303: Iterator iterator = null;
304:
305: /* Creates a new property structure */
306: propertyStructure = new PropertyStructure();
307:
308: /*
309: * Creates an iterator for the meta-properties over
310: * the compound property.
311: */
312: iterator = metaProperty.getMetaProperties();
313:
314: /*
315: * For each meta-property in the current
316: * compound meta-property...
317: */
318: while (iterator.hasNext()) {
319: /* ... get the meta-property ... */
320: MetaProperty aMetaProperty = (MetaProperty) iterator
321: .next();
322:
323: Map newKey = null;
324:
325: /*
326: * If the current meta-property is multi-valued,
327: * stablish the new key
328: */
329: if (metaProperty.isMultiValued()) {
330: newKey = new HashMap();
331: newKey
332: .put(
333: PROPERTY_IDENTIFIER_COLUMN_NAME,
334: ""
335: + rs
336: .getInt(GENERATED_IDENTIFIER_COLUMN_NAME));
337: } else {
338: newKey = getKey();
339: }
340:
341: /*
342: * Add to the property structure the currently processed
343: * meta-property with values
344: */
345: propertyStructure.put((Property) aMetaProperty
346: .accept(new FindMetaPropertyVisitor(
347: getConnection(), rs, newKey)));
348: }
349:
350: /* Add the property structure to a list */
351: values.add(propertyStructure);
352: }
353: } catch (SQLException e) {
354: throw new VisitorException(
355: "ERROR: Database error while getting "
356: + "contents from "
357: + getTableName(metaProperty) + ": "
358: + e.getMessage(), e);
359: }
360:
361: return (PropertyStructure[]) values
362: .toArray(new PropertyStructure[0]);
363: }
364:
365: /**
366: * Gets the values for the properties described by the given
367: * <code>MetaSimpleProperty</code> from the <code>ResultSet</code>.
368: * @param metaProperty Metainformation about the structure of data.
369: * @param rs The values as retrieved from the database.
370: * @return String[] The values for use with <code>Proeprty</code> objects.
371: * @throws VisitorException if the values can't be fetched from the
372: * <code>ResultSet</code>.
373: */
374: protected String[] getValues(MetaSimpleProperty metaProperty,
375: ResultSet rs) throws VisitorException {
376:
377: ArrayList values = new ArrayList();
378:
379: String columnName = SQLPersistenceTypeAnnotationHelper
380: .getColumnNameAnnotation(metaProperty);
381: try {
382: if (metaProperty.isMultiValued()) {
383: /*
384: * For each row retrieved, add the value to a list of values
385: * for this only column
386: */
387: while (rs.next()) {
388: String value = rs.getString(columnName);
389: Object genId = rs
390: .getObject(GENERATED_IDENTIFIER_COLUMN_NAME);
391: if (genId != null) {
392: values.add(value);
393: } else {
394: return new String[0];
395: }
396: }
397: } else {
398: /* retrieve the only value from the current result set */
399: String value = resultSet.getString(columnName);
400: if (value != null) {
401: values.add(value);
402: }
403:
404: }
405: } catch (SQLException e) {
406: throw new VisitorException(
407: "ERROR: Can't retrieve data from a "
408: + "ResultSet for SimpleMetaProperty "
409: + metaProperty.getSimpleName()
410: + " with column name '" + columnName
411: + "' : " + e.getMessage(), e);
412: }
413: /* Convert the list in an array of String's */
414: return (String[]) values.toArray(new String[0]);
415: }
416:
417: /**
418: * Echoes a message to the MyPersonalizer log if debugMode is enabled.
419: *
420: * @param message the message to be displayed.
421: */
422: private void debug(String message) {
423:
424: if (debugMode) {
425: Log mypersonalizerLog = LogManager
426: .getLog(LogNamingConventions.MYPERSONALIZER);
427: mypersonalizerLog.write(message, null,
428: FindMetaPropertyVisitor.class);
429: }
430:
431: }
432:
433: }
|