You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by cb...@apache.org on 2012/03/09 17:23:32 UTC
svn commit: r1298906 [5/14] - in /velocity/sandbox/velosurf: ./ docs/
examples/ examples/ant-vpp/ examples/ant-vpp/lib/ examples/auth-l10n/
examples/auth-l10n/WEB-INF/ examples/auth-l10n/WEB-INF/lib/
examples/auth-l10n/WEB-INF/src/ examples/auth-l10n/e...
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/AttributeReference.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/AttributeReference.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/AttributeReference.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/AttributeReference.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.context;
+
+import java.sql.SQLException;
+import java.util.*;
+import org.apache.velocity.velosurf.model.Attribute;
+import org.apache.velocity.velosurf.util.Logger;
+import org.apache.velocity.velosurf.util.StringLists;
+
+/**
+ * Context wrapper for attributes.
+ *
+ * @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ */
+public class AttributeReference implements Iterable
+{
+ /**
+ * Constructor.
+ *
+ * @param params the parameters map this attribute reference applies to
+ * @param attribute the wrapped attribute
+ */
+ public AttributeReference(Map<String, Object> params, Attribute attribute)
+ {
+ this.params = params;
+ this.attribute = attribute;
+ }
+
+ /**
+ * <p>Refines this attribute's reference result. The provided criterium will be added to the 'where' clause (or a 'where' clause will be added).</p>
+ * <p>This method can be called several times, thus allowing a field-by-field handling of an html search form.</p>
+ * <p>All criteria will be merged with the sql 'and' operator (if there is an initial where clause, it is wrapped into parenthesis).</p>
+ * <p>Example : suppose we have defined the attribute 'person.children' as " *person(person_id):select * from person where parent_id=?". Then, if we issue the following calls from inside the template :</p>
+ * <blockquote>
+ * $bob.children.refine("age>18")
+ * <br>
+ * $bob.children.refine("gender='F'")
+ * </blockquote>
+ * <p>the resulting query that will be issed is :</p>
+ * <p><code>select * from person where (parent_id=?) and (age>18) and (gender='F')</code></p>
+ *
+ * @param criterium a valid sql condition
+ */
+ public void refine(String criterium)
+ {
+ if(refineCriteria == null)
+ {
+ refineCriteria = new ArrayList();
+ }
+ refineCriteria.add(criterium);
+ }
+
+ /**
+ * Clears any refinement made on this attribute.
+ * <p>
+ */
+ public void clearRefinement()
+ {
+ refineCriteria = null;
+ }
+
+ /**
+ * Called by the #foreach directive.
+ * <p>
+ * Returns a RowIterator on all possible instances of this entity, possibly previously refined and ordered.</p>
+ *
+ * @return a RowIterator on instances of this entity, or null if an error
+ * occured.
+ */
+ public Iterator iterator()
+ {
+ try
+ {
+ RowIterator iterator = attribute.query(params, refineCriteria, order);
+
+ return iterator;
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ attribute.getDB().setError(sqle.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Gets all the rows in a list of maps.
+ *
+ * @return a list of all the rows
+ */
+ public List getRows()
+ {
+ try
+ {
+ RowIterator iterator = attribute.query(params, refineCriteria, order);
+
+ return iterator.getRows();
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ attribute.getDB().setError(sqle.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Gets a list of scalars
+ * @return a list of scalars
+ */
+ public List getScalars()
+ {
+ try
+ {
+ RowIterator iterator = attribute.query(params, refineCriteria, order);
+
+ return iterator.getScalars();
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ attribute.getDB().setError(sqle.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Get all the rows in a map firstcol->secondcol.
+ * FIXME: better in Attribute than in AttributeReference
+ * @return a list of all the rows
+ */
+ public Map getMap()
+ {
+ Map result = null;
+
+ try
+ {
+ RowIterator iterator = attribute.query(params, refineCriteria, order);
+ List<String> keys = iterator.keyList();
+
+ if(keys.size() < 2)
+ {
+ Logger.error(".map needs at least two result columns");
+ return null;
+ }
+ if(keys.size() > 2)
+ {
+ Logger.warn(
+ "attribute.map needs only two result columns, only the first two will be taken into account");
+ }
+ result = new TreeMap();
+ while(iterator.hasNext())
+ {
+ Instance i = iterator.next();
+
+ result.put(i.get(keys.get(0)), i.get(keys.get(1)));
+ }
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ attribute.getDB().setError(sqle.getMessage());
+ return null;
+ }
+ return result;
+ }
+
+ /**
+ * Get all the rows in a map firstcol->instance
+ * FIXME: better in Attribute than in AttributeReference
+ * @return a list of all the rows
+ */
+ public Map getInstanceMap()
+ {
+ Map result = null;
+
+ try
+ {
+ RowIterator iterator = attribute.query(params, refineCriteria, order);
+ List<String> keys = iterator.keyList();
+
+ result = new TreeMap();
+ while(iterator.hasNext())
+ {
+ Instance i = iterator.next();
+
+ result.put(i.get(keys.get(0)), i);
+ }
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ attribute.getDB().setError(sqle.getMessage());
+ return null;
+ }
+ return result;
+ }
+
+ /**
+ * <p>Specify an 'order by' clause for this attribute reference result.</p>
+ * <p>If an 'order by' clause is already present in the original query, the new one is appended (but successive calls to this method overwrite previous ones)</p>
+ * <p> postfix " DESC " to a column for descending order.</p>
+ * <p>Pass it null or an empty string to clear any ordering.</p>
+ *
+ * @param order valid sql column names (separated by commas) indicating the
+ * desired order
+ */
+ public void setOrder(String order)
+ {
+ this.order = order;
+ }
+
+ /**
+ * Specified refining criteria defined on this attribute reference.
+ */
+ private List<String> refineCriteria = null;
+
+ /**
+ * Specified 'order by' clause specified for this attribute reference.
+ */
+ private String order = null;
+
+ /**
+ * The map this attribute reference applies to.
+ */
+ private Map<String, Object> params = null;
+
+ /**
+ * The wrapped attribute.
+ */
+ private Attribute attribute = null;
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/DBReference.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/DBReference.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/DBReference.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/DBReference.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.context;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import org.apache.velocity.velosurf.model.Action;
+import org.apache.velocity.velosurf.model.Attribute;
+import org.apache.velocity.velosurf.model.Entity;
+import org.apache.velocity.velosurf.sql.Database;
+import org.apache.velocity.velosurf.util.Logger;
+import org.apache.velocity.velosurf.util.UserContext;
+
+/**
+ * <p>A context wrapper for the main database connection object.</p>
+ * <p>The "<code>$db</code>" context variable is assigned a new instance of this class at each velocity parsing.</p>
+ *
+ * @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ */
+
+// FIXME : right now, extends HashMap bkoz velocity wants a HashMap for setters
+public class DBReference extends HashMap<String, Object> implements HasParametrizedGetter
+{
+ /**
+ * Default constructor for use by derived classes.
+ */
+ protected DBReference(){}
+
+ /**
+ * Constructs a new database reference.
+ *
+ * @param db the wrapped database connection
+ */
+ public DBReference(Database db)
+ {
+ init(db);
+ }
+
+ /**
+ * Protected initialization method.
+ *
+ * @param db database connection
+ */
+ protected void init(Database db)
+ {
+ this.db = db;
+ cache = new HashMap<String, Object>();
+ externalParams = new HashMap<String, Object>();
+ }
+
+ /**
+ * <p>Generic getter, used to access entities or root attributes by their name.</p>
+ * <p>For attributes, the return value depends upon the type of the attribute :</p>
+ * <ul>
+ * <li>if the attribute is multivalued, returns an AttributeReference.
+ * <li>if the attribute is singlevalued, returns an Instance.
+ * <li>if the attribute is scalar, returns a String.
+ * </ul>
+ * <p>If no attribute is found, entities are searched, then external parameters.</p>
+ * @param key the name of the desired entity or root attribute.
+ * @return an entity, an attribute reference, an instance, a string or null
+ * if not found. See See above.
+ */
+ public Object get(Object key)
+ {
+ String property = db.adaptCase((String)key);
+
+ try
+ {
+ Object result = null;
+
+ /* 1) try the cache */
+ result = cache.get(property);
+ if(result != null)
+ {
+ return result;
+ }
+
+ /* 2) try to get a root attribute */
+ Attribute attribute = db.getAttribute(property);
+
+ if(attribute != null)
+ {
+ switch(attribute.getType())
+ {
+ case Attribute.ROWSET :
+ result = new AttributeReference(this, attribute);
+ break;
+ case Attribute.SCALAR :
+ result = attribute.evaluate(this);
+ break;
+ case Attribute.ROW :
+ result = attribute.fetch(this);
+ break;
+ default :
+ Logger.error("Unknown attribute type encountered: db." + property);
+ result = null;
+ }
+ cache.put(property, result);
+ return result;
+ }
+
+ /* 3) try to get a root action */
+ Action action = db.getAction(property);
+
+ if(action != null)
+ {
+ return Integer.valueOf(action.perform(this));
+ }
+
+ /* 4) try to get an entity */
+ Entity entity = db.getEntity(property);
+
+ if(entity != null)
+ {
+ result = new EntityReference(entity);
+ cache.put(property, result);
+ return result;
+ }
+
+ /* 5) try to get an external param */
+ result = externalParams.get(property);
+ if(result != null)
+ {
+ return result;
+ }
+
+ /* 6) try with the user context */
+ result = db.getUserContext().get(property);
+ if(result != null)
+ {
+ return result;
+ }
+
+ /* Sincerely, I don't see... */
+ return null;
+ }
+ catch(SQLException sqle)
+ {
+ db.setError(sqle.getMessage());
+ Logger.log(sqle);
+ return null;
+ }
+ }
+
+ /**
+ * <p>Specific entity getter. </p>
+ * @param key the name of the desired entity or root attribute.
+ * @return an entity, an attribute reference, an instance, a string or null
+ */
+ public EntityReference getEntity(String key)
+ {
+ key = db.adaptCase(key);
+
+ Entity entity = db.getEntity(key);
+
+ if(entity != null)
+ {
+ EntityReference result = new EntityReference(entity);
+
+ cache.put(key, result);
+ return result;
+ }
+ return null;
+ }
+
+ /**
+ * <p>Specific getter for last error message in user context </p>
+ * @return last threaded error message if any or null
+ */
+ public String getError()
+ {
+ return(String)db.getUserContext().get("error");
+ }
+
+ /**
+ * Default method handler, called by Velocity when it did not find the specified method.
+ *
+ * @param key asked key
+ * @param params passed parameters
+ * @see HasParametrizedGetter
+ */
+ public Object getWithParams(String key, Map params)
+ {
+ Object result = null;
+
+ try
+ {
+ String property = db.adaptCase((String)key);
+
+ for(Map.Entry entry : (Set<Map.Entry>)params.entrySet())
+ {
+ externalParams.put(db.adaptCase((String)entry.getKey()), entry.getValue());
+ }
+
+ /* 1) try to get a root attribute */
+ Attribute attribute = db.getAttribute(property);
+
+ if(attribute != null)
+ {
+ switch(attribute.getType())
+ {
+ case Attribute.ROWSET :
+ result = new AttributeReference(this, attribute);
+ break;
+ case Attribute.SCALAR :
+ result = attribute.evaluate(this);
+ break;
+ case Attribute.ROW :
+ result = attribute.fetch(this);
+ break;
+ default :
+ Logger.error("Unknown attribute type encountered: db." + property);
+ }
+ }
+
+ /* 2) try to get a root action */
+ Action action = db.getAction(property);
+
+ if(action != null)
+ {
+ result = Integer.valueOf(action.perform(this)); // TODO actions should return a Long !!!!
+ }
+ }
+ catch(SQLException sqle)
+ {
+ db.setError(sqle.getMessage());
+ Logger.log(sqle);
+ result = null;
+ }
+ return result;
+ }
+
+ /**
+ * Generic setter used to set external params for children attributes.
+ *
+ * @param key name of the external parameter
+ * @param value value given to the external parameter
+ * @return the previous value, if any
+ */
+ public Object put(String key, Object value)
+ {
+ /*
+ * Clear actual values in the cache, because the value of attributes may change...
+ */
+ for(Iterator i = cache.keySet().iterator(); i.hasNext(); )
+ {
+ Object k = i.next();
+ Object v = cache.get(k);
+
+ if(!(v instanceof AttributeReference) &&!(v instanceof EntityReference))
+ {
+ i.remove();
+ }
+ }
+ return externalParams.put(db.adaptCase((String)key), value);
+ }
+
+ /**
+ * Get the schema name.
+ * @return the schema
+ */
+ public String getSchema()
+ {
+ return db.getSchema();
+ }
+
+ /**
+ * Obfuscate the given value.
+ * @param value value to obfuscate
+ *
+ * @return obfuscated value
+ */
+ public String obfuscate(Object value)
+ {
+ return db.obfuscate(value);
+ }
+
+ /**
+ * De-obfuscate the given value.
+ * @param value value to de-obfuscate
+ *
+ * @return obfuscated value
+ */
+ public String deobfuscate(Object value)
+ {
+ return db.deobfuscate(value);
+ }
+
+ /**
+ * User context getter
+ * @return current user context
+ */
+ public UserContext getUserContext()
+ {
+ return db.getUserContext();
+ }
+
+ /**
+ * User context setter
+ * @param userContext user context
+ */
+ public void setUserContext(UserContext userContext)
+ {
+ db.setUserContext(userContext);
+ }
+
+ /**
+ * String representation of this db reference.
+ * For now, returns a list of defined external parameters.
+ */
+ public String toString()
+ {
+ return "[DBRef with root attributes " + db.getRootEntity().getAttributes() + " and external params"
+ + externalParams + "]";
+ }
+
+ /**
+ * The wrapped database connection.
+ */
+ protected Database db = null;
+
+ /**
+ * A cache used by the generic getter. Its purpose is to avoid the creation of several
+ * attribute references for the same multivalued attribute.
+ */
+ private Map<String, Object> cache = null;
+
+ /**
+ * The map of external query parameters used by children attributes.
+ */
+ private Map<String, Object> externalParams = null;
+
+// public void displayStats() { db.displayStats(); }
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/EntityReference.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/EntityReference.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/EntityReference.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/EntityReference.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,482 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.context;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.apache.velocity.velosurf.model.Entity;
+import org.apache.velocity.velosurf.util.Logger;
+import org.apache.velocity.velosurf.util.UserContext;
+
+/**
+ * Context wrapper for an entity.
+ *
+ * @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ */
+public class EntityReference implements Iterable
+{
+ /**
+ * Builds a new EntityReference.
+ *
+ * @param entity the wrapped entity
+ */
+ public EntityReference(Entity entity)
+ {
+ this.entity = entity;
+ }
+
+ /**
+ * gets the name of the wrapped entity
+ */
+ public String getName()
+ {
+ return entity.getName();
+ }
+
+ /**
+ * Insert a new row in this entity's table.
+ *
+ * @param values col -> value map
+ * @return <code>true</code> if successfull, <code>false</code> if an error occurs (in which case $db.error can be checked).
+ */
+ public boolean insert(Map<String, Object> values)
+ {
+ try
+ {
+ return entity.insert(values);
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Returns the ID of the last inserted row (obfuscated if needed).
+ *
+ * @return last insert ID
+ */
+ public Object getLastInsertID()
+ {
+ long id = entity.getDB().getUserContext().getLastInsertedID(entity);
+
+ return entity.filterID(id);
+ }
+
+ /**
+ * <p>Update a row in this entity's table.</p>
+ *
+ * <p>Velosurf will ensure all key columns are specified, to avoid an accidental massive update.</p>
+ *
+ * @param values col -> value map
+ * @return <code>true</code> if successfull, <code>false</code> if an error occurs (in which case $db.error can be checked).
+ */
+ public boolean update(Map<String, Object> values)
+ {
+ try
+ {
+ return entity.update(values);
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * <p>Upsert a row in this entity's table.</p>
+ *
+ * <p>Primary key must be a single column.</p>
+ *
+ * @param values col -> value map
+ * @return <code>true</code> if successfull, <code>false</code> if an error occurs (in which case $db.error can be checked).
+ */
+ public boolean upsert(Map<String, Object> values)
+ {
+ try
+ {
+ return entity.upsert(values);
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * <p>Detele a row from this entity's table.</p>
+ *
+ * <p>Velosurf will ensure all key columns are specified, to avoid an accidental massive update.</p>
+ *
+ * @param values col -> value map
+ * @return <code>true</code> if successfull, <code>false</code> if an error occurs (in which case $db.error can be checked).
+ */
+ public boolean delete(Map<String, Object> values)
+ {
+ try
+ {
+ return entity.delete(values);
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * <p>Detele a row from this entity's table, specifying the value of its unique key column.</p>
+ *
+ * @param keyValue key value
+ * @return <code>true</code> if successfull, <code>false</code> if an error occurs (in which case $db.error can be checked).
+ */
+ public boolean delete(String keyValue)
+ {
+ try
+ {
+ return entity.delete(keyValue);
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * <p>Detele a row from this entity's table, specifying the value of its unique key column.</p>
+ *
+ * <p>Velosurf will ensure all key columns are specified, to avoid an accidental massive update.</p>
+ *
+ * @param keyValue key value
+ * @return <code>true</code> if successfull, <code>false</code> if an error occurs (in which case $db.error can be checked).
+ */
+ public boolean delete(Number keyValue)
+ {
+ try
+ {
+ return entity.delete(keyValue);
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Fetch an Instance of this entity, specifying the values of its key columns in their natural order.
+ *
+ * @param values values of the key columns
+ * @return an Instance, or null if an error occured (in which case
+ * $db.error can be checked)
+ */
+ public Instance fetch(List<Object> values)
+ {
+ try
+ {
+ Instance instance = entity.fetch(values);
+
+ return instance;
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Fetch an Instance of this entity, specifying the values of its key columns in the map.
+ *
+ * @param values key=>value map
+ * @return an Instance, or null if an error occured (in which case
+ * $db.error can be checked)
+ */
+ public Instance fetch(Map<String, Object> values)
+ {
+ try
+ {
+ Instance instance = entity.fetch(values);
+
+ return instance;
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Fetch an Instance of this entity, specifying the value of its unique key column as a string
+ *
+ * @param keyValue value of the key column
+ * @return an Instance, or null if an error occured (in which case
+ * $db.error can be checked)
+ */
+ public Instance fetch(String keyValue)
+ {
+ try
+ {
+ Instance instance = entity.fetch(keyValue);
+
+ return instance;
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Fetch an Instance of this entity, specifying the value of its unique key column as an integer
+ *
+ * @param keyValue value of the key column
+ * @return an Instance, or null if an error occured (in which case
+ * $db.error can be checked)
+ */
+ public Instance fetch(Number keyValue)
+ {
+ try
+ {
+ Instance instance = entity.fetch(keyValue);
+
+ return instance;
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Called by the #foreach directive.
+ *
+ * @return a RowIterator on all instances of this entity, possibly previously
+ * refined or ordered.
+ */
+ public Iterator iterator()
+ {
+ try
+ {
+ RowIterator iterator = entity.query(refineCriteria, order);
+
+ return iterator;
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Get all the rows in a list of maps.
+ *
+ * @return a list of all the rows
+ */
+ public List getRows()
+ {
+ try
+ {
+ RowIterator iterator = entity.query(refineCriteria, order);
+
+ return iterator.getRows();
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ entity.getDB().setError(sqle.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * <p>Refines this entity reference querying result. The provided criterium will be added to the 'where' clause (or a 'where' clause will be added).</p>
+ *
+ * <p>This method can be called several times, thus allowing a field-by-field handling of an html search form.</p>
+ *
+ * <p>All criteria will be merged with the sql 'and' operator (if there is an initial where clause, it is wrapped into parenthesis).</p>
+ *
+ * <p>Example: if we issue the following calls from inside the template:</p>
+ * <blockquote>
+ * $person.refine("age>30")
+ * <br>
+ * $person.refine("salary>3000")
+ * </blockquote>
+ * <p>the resulting query that will be issed is:</p>
+ *
+ * <p><code>select * from person where (age>30) and (salary>3000)</code></p>
+ *
+ * @param criterium a valid sql condition
+ */
+ public void refine(String criterium)
+ {
+ Logger.trace("refine: " + criterium);
+
+ /* protect from SQL query injection */
+
+ // FIXME: check that there is an even number of "'"
+ if( /* criterium.indexOf('\'') != -1 || */criterium.indexOf(';') != -1 || criterium.indexOf("--") != -1)
+ {
+ Logger.error("bad refine string: " + criterium);
+ }
+ else
+ {
+ if(refineCriteria == null)
+ {
+ refineCriteria = new ArrayList();
+ }
+ refineCriteria.add(criterium);
+ }
+ }
+
+ /**
+ * Clears any refinement made on this entity.
+ */
+ public void clearRefinement()
+ {
+ refineCriteria = null;
+ }
+
+ /**
+ * <p>Specify an 'order by' clause for this attribute reference result.</p>
+ * <p>If an 'order by' clause is already present in the original query, the ew one is appended (but successive calls to this method overwrite previous ones).</p>
+ * <p> postfix " DESC " to a column for descending order.</p>
+ * <p>Pass it null or an empty string to clear any ordering.</p>
+ *
+ * @param order valid sql column names (separated by commas) indicating the
+ * desired order
+ */
+ public void setOrder(String order)
+ {
+ /* protect from SQL query injection */
+ if(order.indexOf('\'') != -1 || order.indexOf(';') != -1 || order.indexOf("--") != -1)
+ {
+ Logger.error("bad order string: " + order);
+ }
+ else
+ {
+ this.order = order;
+ }
+ }
+
+ /**
+ * Create a new instance for this entity.
+ *
+ * @return the newly created instance
+ */
+ public Instance newInstance()
+ {
+ Instance instance = entity.newInstance();
+
+ return instance;
+ }
+
+ /**
+ * Build a new instance from a Map object.
+ *
+ * @param values the Map object containing the values
+ * @return the newly created instance
+ */
+ public Instance newInstance(Map<String, Object> values)
+ {
+ Instance instance = entity.newInstance(values);
+
+ return instance;
+ }
+
+ /**
+ * Validate values of this instance.
+ * @param values
+ * @return true if values are valid with respect to defined column constraints
+ */
+ public boolean validate(Map<String, Object> values)
+ {
+ try
+ {
+ return entity.validate(values);
+ }
+ catch(SQLException sqle)
+ {
+ Logger.error("could not check data validity!");
+ entity.getDB().getUserContext().addValidationError("internal errror");
+ Logger.log(sqle);
+ return false;
+ }
+ }
+
+ /**
+ * Getter for the list of column names.
+ *
+ * @return the list of column names
+ */
+ public List getColumns()
+ {
+ return entity.getColumns();
+ }
+
+ public long getCount()
+ {
+ return entity.getCount(refineCriteria);
+ }
+
+ /**
+ * The wrapped entity.
+ */
+ private Entity entity = null;
+
+ /**
+ * Specified order.
+ */
+ private String order = null;
+
+ /**
+ * Specified refining criteria.
+ */
+ private List<String> refineCriteria = null;
+
+ /**
+ * toString, used for debugging
+ */
+ public String toString()
+ {
+ return "[" + getName() + " with attributes: " + entity.getAttributes().keySet() + "]";
+ }
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/ExternalObjectWrapper.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/ExternalObjectWrapper.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/ExternalObjectWrapper.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/ExternalObjectWrapper.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,640 @@
+package org.apache.velocity.velosurf.context;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.velocity.velosurf.model.Entity;
+import org.apache.velocity.velosurf.util.Logger;
+
+/**
+ * <p>This wrapper allows one to specify custom mapping objects that don't inherit from Instance.</p>
+ * <p>For now, the introspection is rather basic but may work for standard getters without ambiguity.</p>
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class ExternalObjectWrapper extends Instance
+{
+ /**
+ * Builds a new PlaiObjectWrapper.
+ *
+ * @param entity the related entity
+ * @param object target object
+ */
+ public ExternalObjectWrapper(Entity entity, Object object)
+ {
+ super(entity);
+ wrapped = object;
+
+ Class clazz = wrapped.getClass();
+
+ classInfo = classInfoMap.get(clazz.getName());
+ if(classInfo == null)
+ {
+ classInfo = new ClassInfo(clazz);
+ classInfoMap.put(clazz.getName(), classInfo);
+ }
+ }
+
+ /**
+ * Wrapper generic getter. Tries first to get the property from the wrapped object, and falls back to the superclass
+ * if not found.
+ *
+ * @param key key of the property to be returned
+ * @return a String, an Instance, an AttributeReference or null if not found or if an error
+ * occurs
+ */
+ public Object get(Object key)
+ {
+ Object ret = getExternal(key);
+
+ if(ret == null)
+ {
+ ret = super.get(key);
+ }
+ return ret;
+ }
+
+ /**
+ * External getter. Get a value on the external object
+ *
+ * @param key key of the property to be returned
+ * @return a String, an Instance, an AttributeReference or null if not found or if an error
+ * occurs
+ */
+ public Object getExternal(Object key)
+ {
+ Method m = classInfo.getGetter((String)key);
+
+ if(m != null)
+ {
+ try
+ {
+ return m.invoke(wrapped, m.getParameterTypes().length > 0 ? new Object[] { key } : new Object[] {}); // return even if result is null
+ }
+ catch(Exception e)
+ {
+ Logger.warn("could not get value of field " + getEntity().getName() + "." + key
+ + "... falling back to the Instance getter");
+ Logger.log(e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Wrapper generic setter. Tries first to set the property into the wrapped object, and falls back to the superclass
+ * if not found.
+ * @param key key of the property to be set
+ * @param value corresponding value
+ * @return previous value, or null
+ */
+ public Object put(String key, Object value)
+ {
+ Method m = classInfo.getSetter((String)key);
+
+ if(m != null)
+ {
+ try
+ {
+ return m.invoke(wrapped,
+ m.getParameterTypes().length == 2
+ ? new Object[] { key, value } : new Object[] { value });
+ }
+ catch(Exception e)
+ {
+ Logger.warn("could not set value of field " + getEntity().getName() + "." + key + " to '" + value
+ + "'... falling back to the Instance setter");
+ Logger.log(e);
+ }
+ }
+ return super.put(key, value);
+ }
+
+ /**
+ * <p>Try to update the row associated with this Instance using an update() method
+ * in the external object.</p>
+ *
+ * @return <code>true</code> if successfull, <code>false</code> if an error
+ * occurs (in which case $db.error can be checked).
+ */
+ public boolean update()
+ {
+ Method m = classInfo.getUpdate1();
+
+ if(m != null)
+ {
+ Class cls = m.getReturnType();
+ Object args[] = {};
+
+ try
+ {
+ if(cls == boolean.class || cls == Boolean.class)
+ {
+ return((Boolean)m.invoke(wrapped, args)).booleanValue();
+ }
+ else
+ {
+ Logger.warn(
+ "external object wrapper: update method should return boolean or Boolean. Actual result will be ignored.");
+ m.invoke(wrapped, args);
+ return true;
+ }
+ }
+ catch(Exception e)
+ {
+ Logger.warn("method" + cls.getName() + ".update() throw exception: " + e);
+ return false;
+ }
+ }
+ else
+ {
+ return super.update();
+ }
+ }
+
+ /**
+ * <p>Try to update the row associated with this Instance using an update(map) method
+ * in the external object.</p>
+ *
+ * @return <code>true</code> if successfull, <code>false</code> if an error
+ * occurs (in which case $db.error can be checked).
+ */
+ public boolean update(Map values)
+ {
+ Method m = classInfo.getUpdate2();
+
+ if(m != null)
+ {
+ Class cls = m.getReturnType();
+ Object args[] = { values };
+
+ try
+ {
+ if(cls == boolean.class || cls == Boolean.class)
+ {
+ return((Boolean)m.invoke(wrapped, args)).booleanValue();
+ }
+ else
+ {
+ Logger.warn(
+ "external object wrapper: update method should return boolean or Boolean. Actual result will be ignored.");
+ m.invoke(wrapped, args);
+ return true;
+ }
+ }
+ catch(Exception e)
+ {
+ Logger.warn("method" + cls.getName() + ".update() throw exception: " + e);
+ return false;
+ }
+ }
+ else
+ {
+ return super.update();
+ }
+ }
+
+ /**
+ * <p>Tries to delete the row associated with this Instance using a delete() method in the external object.
+ * Velosurf will ensure all key columns are specified, to avoid an accidental massive update.</p>
+ *
+ * @return <code>true</code> if successfull, <code>false</code> if an error
+ * occurs (in which case $db.error can be checked).
+ */
+ public boolean delete()
+ {
+ Method m = classInfo.getDelete();
+
+ if(m != null)
+ {
+ Class cls = m.getReturnType();
+ Object args[] = {};
+
+ try
+ {
+ if(cls == boolean.class || cls == Boolean.class)
+ {
+ return((Boolean)m.invoke(wrapped, args)).booleanValue();
+ }
+ else
+ {
+ Logger.warn(
+ "external object wrapper: delete method should return boolean or Boolean. Actual result will be ignored.");
+ m.invoke(wrapped, args);
+ return true;
+ }
+ }
+ catch(Exception e)
+ {
+ Logger.warn("method" + cls.getName() + ".delete() throw exception: " + e);
+ return false;
+ }
+ }
+ else
+ {
+ return super.delete();
+ }
+ }
+
+ /**
+ * Tries to insert a new row corresponding to this Instance using an insert() method in the external object.
+ *
+ * @return <code>true</code> if successfull, <code>false</code> if an error
+ * occurs (in which case $db.error can be checked).
+ */
+ public boolean insert()
+ {
+ Method m = classInfo.getInsert();
+
+ if(m != null)
+ {
+ Class cls = m.getReturnType();
+ Object args[] = {};
+
+ try
+ {
+ if(cls == boolean.class || cls == Boolean.class)
+ {
+ return((Boolean)m.invoke(wrapped, args)).booleanValue();
+ }
+ else
+ {
+ Logger.warn(
+ "external object wrapper: insert method should return boolean or Boolean. Actual result will be ignored.");
+ m.invoke(wrapped, args);
+ return true;
+ }
+ }
+ catch(Exception e)
+ {
+ Logger.warn("method" + cls.getName() + ".delete() throw exception: " + e);
+ return false;
+ }
+ }
+ else
+ {
+ return super.insert();
+ }
+ }
+
+ /**
+ * Returns the underlying external object.
+ *
+ * @return the external object
+ */
+ public Object unwrap()
+ {
+ return wrapped;
+ }
+
+ /** The wrapped object. */
+ Object wrapped = null;
+
+ /** Info on the wrapped object class. */
+ ClassInfo classInfo = null;
+
+ /** A map of class infos. */
+ static Map<String, ClassInfo> classInfoMap = new HashMap<String, ClassInfo>();
+
+ /** A cache for the wrapped object getter methods. */
+ Map getterCache = null;
+
+ /** A cache for the wrapped object setter methods. */
+ Map setterCache = null;
+
+ /** This private class gathers informations on the class of wrapped objects. */
+ static private class ClassInfo
+ {
+ ClassInfo(Class clazz)
+ {
+ this.clazz = clazz;
+ }
+
+ /**
+ * Getter getter :-) .
+ * @param key property name
+ * @return property getter, if found
+ */
+ Method getGetter(String key)
+ {
+ Method result = (Method)getterMap.get(key);
+
+ if(result == noSuchMethod)
+ {
+ return null;
+ }
+ if(result != null)
+ {
+ return result;
+ }
+
+ Class[] types = {};
+
+ // getFoo
+ StringBuffer sb = new StringBuffer("get");
+
+ sb.append(key);
+ try
+ {
+ result = clazz.getMethod(sb.toString(), types);
+ getterMap.put(key, result);
+ return result;
+ }
+ catch(NoSuchMethodException nsme) {}
+
+ // getfoo
+ char c = sb.charAt(3);
+
+ sb.setCharAt(3, Character.isLowerCase(c) ? Character.toUpperCase(c) : Character.toLowerCase(c));
+ try
+ {
+ result = clazz.getMethod(sb.toString(), types);
+ getterMap.put(key, result);
+ return result;
+ }
+ catch(NoSuchMethodException nsme) {}
+
+ // get(foo)
+ result = getGenericGetter();
+ if(result == null)
+ {
+ getterMap.put(key, noSuchMethod);
+ }
+ else
+ {
+ getterMap.put(key, result);
+ }
+ return result;
+ }
+
+ /**
+ * Setter getter
+ * @param key property name
+ * @return property setter, if found
+ */
+ Method getSetter(String key)
+ {
+ Method result = setterMap.get(key);
+
+ if(result == noSuchMethod)
+ {
+ return null;
+ }
+ if(result != null)
+ {
+ return result;
+ }
+
+ Class[] types = {};
+
+ /* setFoo */
+ StringBuffer sb = new StringBuffer("set");
+
+ sb.append(key);
+ try
+ {
+ result = clazz.getMethod(sb.toString(), types);
+ setterMap.put(key, result);
+ return result;
+ }
+ catch(NoSuchMethodException nsme) {}
+
+ /* setfoo */
+ char c = sb.charAt(3);
+
+ sb.setCharAt(3, Character.isLowerCase(c) ? Character.toUpperCase(c) : Character.toLowerCase(c));
+ try
+ {
+ result = clazz.getMethod(sb.toString(), types);
+ setterMap.put(key, result);
+ return result;
+ }
+ catch(NoSuchMethodException nsme) {}
+
+ /* put(foo,bar) */
+ result = getGenericSetter();
+ if(result == null)
+ {
+ setterMap.put(key, noSuchMethod);
+ }
+ else
+ {
+ setterMap.put(key, result);
+ }
+ return result;
+ }
+
+ /**
+ * Tries to get an update() method in the wrapped object.
+ * @return found update method, if any
+ */
+ Method getUpdate1()
+ {
+ if(update1 == noSuchMethod)
+ {
+ return null;
+ }
+ if(update1 != null)
+ {
+ return update1;
+ }
+ try
+ {
+ return update1 = clazz.getMethod("update", new Class[] {});
+ }
+ catch(NoSuchMethodException nsme)
+ {
+ update1 = noSuchMethod;
+ return null;
+ }
+ }
+
+ /**
+ * Tries to get an update(Map) method in the wrapped object.
+ * @return found update method, if any
+ */
+ Method getUpdate2()
+ {
+ if(update2 == noSuchMethod)
+ {
+ return null;
+ }
+ if(update2 != null)
+ {
+ return update2;
+ }
+ try
+ {
+ return update2 = clazz.getMethod("update", new Class[] { Map.class });
+ }
+ catch(NoSuchMethodException nsme)
+ {
+ update2 = noSuchMethod;
+ return null;
+ }
+ }
+
+ /**
+ * Tries to get an insert() method in the wrapped object.
+ * @return found method, if any
+ */
+ Method getInsert()
+ {
+ if(insert == noSuchMethod)
+ {
+ return null;
+ }
+ if(insert != null)
+ {
+ return insert;
+ }
+ try
+ {
+ return insert = clazz.getMethod("update", new Class[] { Map.class });
+ }
+ catch(NoSuchMethodException nsme)
+ {
+ insert = noSuchMethod;
+ return null;
+ }
+ }
+
+ /**
+ * Tries to get a delete() method in the wrapped object.
+ * @return found method, if any
+ */
+ Method getDelete()
+ {
+ if(delete == noSuchMethod)
+ {
+ return null;
+ }
+ if(delete != null)
+ {
+ return delete;
+ }
+ try
+ {
+ return delete = clazz.getMethod("update", new Class[] { Map.class });
+ }
+ catch(NoSuchMethodException nsme)
+ {
+ delete = noSuchMethod;
+ return null;
+ }
+ }
+
+ /**
+ * Tries to get a generic getter in the wrapped object.
+ * @return found method, if any
+ */
+ Method getGenericGetter()
+ {
+ if(genericGetter == noSuchMethod)
+ {
+ return null;
+ }
+ if(genericGetter != null)
+ {
+ return genericGetter;
+ }
+
+ Class[] types = new Class[] { Object.class };
+
+ try
+ {
+ return genericGetter = clazz.getMethod("get", types);
+ }
+ catch(NoSuchMethodException nsme)
+ {
+ genericGetter = noSuchMethod;
+ return null;
+ }
+ }
+
+ /**
+ * Tries to get a generic setter in the wrapped object.
+ * @return found method, if any
+ */
+ Method getGenericSetter()
+ {
+ if(genericSetter == noSuchMethod)
+ {
+ return null;
+ }
+ if(genericSetter != null)
+ {
+ return genericSetter;
+ }
+
+ Class[] types = new Class[] { Object.class, Object.class };
+
+ try
+ {
+ return genericSetter = clazz.getMethod("put", types);
+ }
+ catch(NoSuchMethodException nsme)
+ {
+ genericSetter = noSuchMethod;
+ return null;
+ }
+ }
+
+ /**
+ * Wrapped class.
+ */
+ Class clazz;
+
+ /**
+ * Getter map.
+ */
+ Map<String, Method> getterMap = new HashMap<String, Method>();
+
+ /**
+ * Setter map.
+ */
+ Map<String, Method> setterMap = new HashMap<String, Method>();
+
+ /**
+ * Generic getter.
+ */
+ Method genericGetter = null;
+
+ /**
+ * Generic setter.
+ */
+ Method genericSetter = null;
+
+ /**
+ * Update method, first form.
+ */
+ Method update1 = null;
+
+ /**
+ * Update method, second form.
+ */
+ Method update2 = null;
+
+ /**
+ * Insert method.
+ */
+ Method insert = null;
+
+ /**
+ * Delete method.
+ */
+ Method delete = null;
+
+ /* dummy method object used to remember we already tried to find an unexistant method. */
+ static Method noSuchMethod;
+
+ static
+ {
+ try
+ {
+ noSuchMethod = Object.class.getMethod("toString", new Class[] {});
+ }
+ catch(NoSuchMethodException nsme) {}
+ }
+ }
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/HasParametrizedGetter.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/HasParametrizedGetter.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/HasParametrizedGetter.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/HasParametrizedGetter.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,20 @@
+package org.apache.velocity.velosurf.context;
+
+import java.util.Map;
+
+/**
+ * Implemented by context objects that do provide a default method handler. This is used
+ * to let template designers provide extra external parameters to an attribute or an action,
+ * like: <code>$myinstance.myattribute({'param1':'value1','param2':'value2'})</code>
+ *
+ */
+public interface HasParametrizedGetter
+{
+ /**
+ * Default method handler, called by Velocity when it did not find the specified method
+ *
+ * @param key asked key
+ * @param params passed parameters
+ */
+ public Object getWithParams(String key, Map params);
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/Instance.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/Instance.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/Instance.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/Instance.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,682 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.context;
+
+import java.sql.SQLException;
+import java.util.*;
+import org.apache.velocity.velosurf.model.Action;
+import org.apache.velocity.velosurf.model.Attribute;
+import org.apache.velocity.velosurf.model.Entity;
+import org.apache.velocity.velosurf.sql.Database;
+import org.apache.velocity.velosurf.sql.PooledPreparedStatement;
+import org.apache.velocity.velosurf.util.Logger;
+import org.apache.velocity.velosurf.util.StringLists;
+import org.apache.velocity.velosurf.util.UserContext;
+
+/**
+ * An Instance provides field values by their name.
+ *
+ * @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ */
+public class Instance extends TreeMap<String,Object> implements HasParametrizedGetter
+{
+
+ /**
+ * Build an empty instance for the given entity.
+ * The method initialize(Entity) should be called afterwards.
+ */
+ public Instance()
+ {
+ }
+
+
+ /**
+ * Build an empty instance for the given entity.
+ * This is the only constructor that will keep columns in their natural order
+ * (others will sort columns alphabetically)
+ *
+ * @param entity Entity this instance is a realisation of
+ */
+ public Instance(Entity entity)
+ {
+ super(entity.getColumnOrderComparator());
+ initialize(entity);
+ }
+
+ /**
+ * Builds a generic instance using <code>values</code>.
+ * @param values
+ */
+ public Instance(Map<String,Object> values)
+ {
+ this(values,null);
+ }
+
+ /**
+ * Builds a generic instance using <code>values</code>.
+ * @param values
+ * @param db
+ */
+ public Instance(Map<String,Object> values, Database db)
+ {
+ this.db = db;
+ for(Object key:values.keySet())
+ {
+ put(Database.adaptContextCase((String)key),values.get(key));
+ }
+ }
+
+ /**
+ * Initialization. Meant to be overloaded if needed.
+ * @param entity
+ */
+ public void initialize(Entity entity)
+ {
+ this.entity = entity;
+ db = this.entity.getDB();
+ localized = this.entity.hasLocalizedColumns();
+ dirtyFlags = new ArrayList();
+ for(int i=0; i<entity.getUpdatableColumns().size();i++)
+ {
+ dirtyFlags.add(false);
+ }
+ }
+
+ /**
+ * Get this Instance's Entity.
+ *
+ * @return this Instance's Entity.
+ */
+ public EntityReference getEntity()
+ {
+ return new EntityReference(entity);
+ }
+
+ /**
+ * <p>Returns an ArrayList of two-entries maps ('name' & 'value'), meant to be use in a #foreach loop to build form fields.</p>
+ * <p>Example:</p>
+ * <code>
+ * #foreach ($field in $product.primaryKey)<br>
+ * <input type=hidden name='$field.name' value='$field.value'><br>
+ * #end</code>
+ * <p>Please note that this method won't be of any help if you are using column aliases.</p>
+ *
+ * @return an ArrayList of two-entries maps ('name' & 'value')
+ */
+ public List getPrimaryKey()
+ {
+ List<Map<String,Object>> result = new ArrayList<Map<String,Object>>();
+ if (entity!=null)
+ {
+ for (Iterator i=entity.getPKCols().iterator();i.hasNext();)
+ {
+ String key = (String)i.next();
+ Map<String,Object> map = new HashMap<String,Object>();
+ map.put("name",key);
+ map.put("value",getInternal(key));
+ result.add(map);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * <p>Generic getter, used to access this instance properties by their name.</p>
+ * <p>Asked property is first searched in the Map, then among Attributes defined for the entity.</p>
+ *
+ * @param k key of the property to be returned
+ * @return a String, an Instance, an AttributeReference or null if an error
+ * occurs
+ */
+ public Object get(Object k)
+ {
+ String key = resolveName((String)k);
+ Object result = null;
+ try
+ {
+ result = super.get(key);
+ if (result == null)
+ {
+ if (entity!=null)
+ {
+ Attribute attribute = entity.getAttribute(key);
+ if (attribute != null)
+ {
+ switch (attribute.getType())
+ {
+ case Attribute.ROWSET:
+ result = new AttributeReference(this,attribute);
+ // then cache it in the map, so that order and refinement will work later in the same context
+ super.put(key,result);
+ break;
+ case Attribute.ROW:
+ result = attribute.fetch(this);
+ if(attribute.getCaching())
+ {
+ super.put(key,result);
+ }
+ break;
+ case Attribute.SCALAR:
+ result = attribute.evaluate(this);
+ if(attribute.getCaching())
+ {
+ super.put(key,result);
+ }
+ break;
+ default:
+ Logger.error("Unknown attribute type for "+entity.getName()+"."+key+"!");
+ }
+ }
+ else
+ {
+ Action action = entity.getAction(key);
+ if (action != null)
+ {
+ result = action.perform(this);
+ }
+ }
+ }
+ }
+ else if (localized && entity.isLocalized(key))
+ {
+ result = db.getUserContext().localize(result.toString());
+ }
+ }
+ catch (SQLException sqle)
+ {
+ handleSQLException(sqle);
+ }
+ return result;
+ }
+
+ /**
+ * Default method handler, called by Velocity when it did not find the specified method.
+ *
+ * @param key asked key
+ * @param params passed parameters
+ * @see HasParametrizedGetter
+ */
+ public Object getWithParams(String key,Map params)
+ {
+ for(Map.Entry entry: (Set<Map.Entry>)params.entrySet())
+ {
+ put(db.adaptCase((String)entry.getKey()),entry.getValue());
+ }
+ return get(db.adaptCase(key));
+ }
+
+ /**
+ * Generic setter.
+ *
+ * @param key key of the property to be set
+ * @param value corresponding value
+ * @return previous value, or null
+ */
+ public synchronized Object put(String key, Object value)
+ {
+ key = resolveName(key);
+ int index;
+ if (entity != null)
+ {
+ value = entity.filterIncomingValue(key,value);
+ if ( (index = entity.getUpdatableColumnIndex(key) ) != -1)
+ {
+ dirtyFlags.set(index,true);
+ }
+ }
+ return super.put(key,value);
+ }
+
+ public synchronized void setClean()
+ {
+ if(dirtyFlags != null)
+ {
+ for(int i=0;i<dirtyFlags.size();i++)
+ {
+ dirtyFlags.set(i,false);
+ }
+ }
+ }
+
+ /**
+ * Global setter that will only set values the correspond to actual
+ * columns (otherwise, use putAll(Map values)).
+ *
+ * @param values corresponding values
+ */
+
+ public synchronized void setColumnValues(Map<String,Object> values)
+ {
+ if(entity == null)
+ {
+ Logger.warn("instance.setColumnValues(map) cannot be used when entity is null");
+ return;
+ }
+ int index;
+ for(Map.Entry<String,Object> entry:values.entrySet())
+ {
+ if( entity.isColumn(entity.resolveName(entry.getKey())))
+ {
+ put(entry.getKey(),entry.getValue());
+ }
+ }
+ }
+
+ /**
+ * Internal getter. First tries on the external object then on the Map interface.
+ *
+ * @param key key of the property to be returned
+ * @return a String, an Instance, an AttributeReference or null if not found or if an error
+ * occurs
+ */
+ public Object getInternal(Object key)
+ {
+ Object ret = getExternal(key);
+ if (ret == null) ret = super.get(key);
+ return ret;
+ }
+
+ /**
+ * External getter. Meant to be overloaded in ExternalObjectWrapper.
+ *
+ * @param key key of the property to be returned
+ * @return a String, an Instance, an AttributeReference or null if not found or if an error
+ * occurs
+ */
+ public Object getExternal(Object key)
+ {
+ return null;
+ }
+
+ /**
+ * Test equality of two instances.
+ * @param o other instance
+ * @return equality status
+ */
+ public boolean equals(Object o)
+ {
+ return super.equals(o);
+ }
+
+ /**
+ * <p>Update the row associated with this Instance from passed values.</p>
+ * <p>Velosurf will ensure all key columns are specified, to avoid an accidental massive update.</p>
+ *
+ * @return <code>true</code> if successfull, <code>false</code> if an error
+ * occurs (in which case $db.error can be checked).
+ */
+ public synchronized boolean update()
+ {
+ try
+ {
+ if (entity == null)
+ {
+ throw new SQLException("Cannot update an instance whose Entity is null.");
+ }
+ if (entity.isReadOnly())
+ {
+ throw new SQLException("Entity "+entity.getName()+" is read-only.");
+ }
+
+ List<String> updateClause = new ArrayList<String>();
+ List<String> whereClause = new ArrayList<String>();
+ List<Object> params = new ArrayList<Object>();
+ List<String> cols = entity.getUpdatableColumns();
+ for (int c = 0; c < cols.size(); c++)
+ {
+ String col = cols.get(c);
+ if(dirtyFlags.get(c))
+ {
+ Object value = getInternal(col);
+ if (value!=null)
+ {
+ updateClause.add(col+"=?");
+ if (entity.isObfuscated(col))
+ {
+ value = entity.deobfuscate(value);
+ }
+ params.add(value);
+ } // TODO else " = null " ? May be a configuration option.
+ }
+ }
+ if(updateClause.size() ==0)
+ {
+ Logger.warn("update of instance '"+entity.getName()+"' all non-key columns are null or non-dirty - no update will be performed");
+ // return true anyway ?
+ return true;
+ }
+ for (String col:entity.getPKCols())
+ {
+ Object value = getInternal(col);
+ if (value == null) throw new SQLException("field '"+col+"' belongs to primary key and cannot be null!");
+ if (entity.isObfuscated(col)) value = entity.deobfuscate(value);
+// if (entity.isLocalized(col)) value = entity.unlocalize(value); ???
+ whereClause.add(col+"=?");
+ params.add(value);
+ }
+ String query = "update "+entity.getTableName()+" set "+StringLists.join(updateClause,",")+" where "+StringLists.join(whereClause," and ");
+ PooledPreparedStatement statement = db.prepare(query);
+ int nb = statement.update(params);
+ if (nb==0)
+ {
+ Logger.warn("query \""+query+"\" affected 0 row...");
+ }
+ else if (nb>1)
+ { // ?!?! Referential integrities on key columns should avoid this...
+ throw new SQLException("query \""+query+"\" affected more than 1 rows!");
+ }
+ else
+ {
+ /* invalidate cache */
+ if (entity != null)
+ {
+ entity.invalidateInstance(this);
+ }
+ }
+ setClean();
+ return true;
+ }
+ catch (SQLException sqle)
+ {
+ handleSQLException(sqle);
+ return false;
+ }
+ }
+
+ /**
+ * <p>Update the row associated with this Instance from actual values.</p>
+ * <p>Velosurf will ensure all key columns are specified, to avoid an accidental massive update.</p>
+ *
+ * @param values values to be used for the update
+ * @return <code>true</code> if successfull, <code>false</code> if an error
+ * occurs (in which case $db.error can be checked).
+ */
+ public synchronized boolean update(Map<String,Object> values)
+ {
+ if (values != null && values != this)
+ {
+ setColumnValues(values);
+ }
+ return update();
+ }
+
+ /**
+ * <p>Delete the row associated with this Instance.</p>
+ * <p>Velosurf will ensure all key columns are specified, to avoid an accidental massive update.</p>
+ *
+ * @return <code>true</code> if successfull, <code>false</code> if an error
+ * occurs (in which case $db.error can be checked).
+ */
+ public synchronized boolean delete()
+ {
+ try
+ {
+ if (entity == null)
+ {
+ throw new SQLException("Instance.delete: Error: Entity is null!");
+ }
+ List<String> whereClause = new ArrayList<String>();
+ List<Object> params = new ArrayList<Object>();
+ for (String col:entity.getPKCols())
+ {
+ Object value = getInternal(col);
+ if (value == null) throw new SQLException("Instance.delete: Error: field '"+col+"' belongs to primary key and cannot be null!");
+ if (entity.isObfuscated(col)) value = entity.deobfuscate(value);
+ whereClause.add(col+"=?");
+ params.add(value);
+ }
+ String query = "delete from "+entity.getTableName()+" where "+StringLists.join(whereClause," and ");
+ PooledPreparedStatement statement = db.prepare(query);
+ int nb = statement.update(params);
+ if (nb==0)
+ {
+ Logger.warn("query \""+query+"\" affected 0 row...");
+ }
+ else if (nb>1) // ?!?! Referential integrities on key columns should avoid this...
+ {
+ throw new SQLException("query \""+query+"\" affected more than 1 rows!");
+ }
+ else
+ {
+ /* invalidate cache */
+ if (entity != null)
+ {
+ entity.invalidateInstance(this);
+ }
+ }
+ return true;
+ }
+ catch (SQLException sqle)
+ {
+ handleSQLException(sqle);
+ return false;
+ }
+ }
+
+ /**
+ * Insert a new row corresponding to this Instance.
+ *
+ * @return <code>true</code> if successfull, <code>false</code> if an error
+ * occurs (in which case $db.error can be checked).
+ */
+ public synchronized boolean insert()
+ {
+ try
+ {
+ if (entity == null)
+ {
+ throw new SQLException("Instance.insert: Error: Entity is null!");
+ }
+
+ if (!entity.validate(this))
+ {
+ return false;
+ }
+ List<String> colsClause = new ArrayList<String>();
+ List<String> valsClause = new ArrayList<String>();
+ List<Object> params = new ArrayList<Object>();
+ List<String> cols = entity.getColumns();
+ for (String col:cols)
+ {
+ Object value = getInternal(col);
+ if (value!=null)
+ {
+ colsClause.add(col);
+ valsClause.add("?");
+ if (entity.isObfuscated(col))
+ {
+ value = entity.deobfuscate(value);
+ }
+ params.add(value);
+ }
+ }
+ String query = "insert into "+entity.getTableName()+" ("+StringLists.join(colsClause,",")+") values ("+StringLists.join(valsClause,",")+")";
+ PooledPreparedStatement statement = db.prepare(query);
+ statement.update(params);
+ List<String> keys = entity.getPKCols();
+ if (keys.size() == 1)
+ {
+ /* What if the ID is not autoincremented? TODO check it. => reverse engineering of autoincrement, and set the value in the instance itself */
+ String keycol = keys.get(0);
+ long newid = statement.getLastInsertID();
+ db.getUserContext().setLastInsertedID(entity,newid);
+ if(getInternal(keycol) == null)
+ {
+ put(keycol,entity.isObfuscated(keycol)?entity.obfuscate(newid):newid);
+ }
+ }
+ setClean();
+ return true;
+ }
+ catch (SQLException sqle)
+ {
+ handleSQLException(sqle);
+ return false;
+ }
+ }
+
+ /**
+ * Validate this instance against declared contraints.
+ * @return a boolean stating whether this instance data are valid in regard to declared constraints
+ */
+ public boolean validate()
+ {
+ try
+ {
+ return entity.validate(this);
+ }
+ catch(SQLException sqle)
+ {
+ handleSQLException(sqle);
+ return false;
+ }
+ }
+
+ /**
+ * Handle an sql exception.
+ *
+ */
+ private void handleSQLException(SQLException sqle)
+ {
+ Logger.log(sqle);
+ db.setError(sqle.getMessage());
+ }
+
+ protected String resolveName(String name)
+ {
+ if(entity != null)
+ {
+ return entity.resolveName(name);
+ }
+ else if (db != null)
+ {
+ return db.adaptCase(name);
+ }
+ else
+ {
+ return name;
+ }
+ }
+
+ /**
+ * Check for a key
+ *
+ * @return whether or not this key is present
+ */
+ public boolean containsKey(Object key)
+ {
+ return super.containsKey(resolveName((String)key));
+ }
+
+ /**
+ * Removes an association
+ *
+ * @return the removed object, or null
+ */
+ public Object remove(Object key)
+ {
+ return super.remove(resolveName((String)key));
+ }
+
+ /**
+ * This Instance's Entity.
+ */
+ protected Entity entity = null;
+
+ /**
+ * Is there a column to localize?
+ */
+ private boolean localized = false;
+
+ /**
+ * The main database connection.
+ */
+ protected Database db = null;
+
+ /**
+ * Keep a dirty flag per column
+ */
+ protected List<Boolean> dirtyFlags = null;
+
+ /**
+ Inherit toString to avoid listing cached AttributeReference
+ */
+ public String toString()
+ {
+ StringBuffer ret = new StringBuffer("{");
+ boolean comma = false;
+ for(Map.Entry<String,Object> entry:super.entrySet())
+ {
+ if(comma)
+ {
+ ret.append(", ");
+ }
+ else
+ {
+ comma = true;
+ }
+ if(!(entry.getValue() instanceof AttributeReference))
+ {
+ ret.append(entry.getKey());
+ ret.append("=");
+ ret.append(entry.getValue());
+ }
+ }
+ ret.append("}");
+ return ret.toString();
+ }
+
+ /**
+ * Insert or update, depending on whether or not a value for the id key is present and does exist
+ *
+ * @return success flag
+ */
+ public synchronized boolean upsert()
+ {
+ List<Map<String,Object>> primkey = getPrimaryKey();
+ if(primkey.size() != 1)
+ {
+ Logger.error("Instance.upsert: singleton primary key expected"); // TODO CB - should throw/catch for homogeneity
+ return false;
+ }
+ Object keyVal = primkey.get(0).get("value");
+ if(keyVal == null)
+ {
+ return insert();
+ }
+ else
+ {
+ Instance previous = getEntity().fetch(String.valueOf(keyVal)); // CB -TODO: there should be an Entity.fetch(Object) method
+ return previous == null ? insert() : update();
+ }
+ }
+
+ /**
+ * Insert or update, depending on whether or not a value for the id key is present and does exist
+ */
+ public synchronized boolean upsert(Map<String,Object> values)
+ {
+ if (values != null && values != this)
+ {
+ setColumnValues(values);
+ }
+ return upsert();
+ }
+
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/RowIterator.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/RowIterator.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/RowIterator.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/RowIterator.java Fri Mar 9 16:23:25 2012
@@ -0,0 +1,383 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.apache.velocity.velosurf.context;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.velocity.velosurf.model.Attribute;
+import org.apache.velocity.velosurf.model.Entity;
+import org.apache.velocity.velosurf.sql.PooledStatement;
+import org.apache.velocity.velosurf.sql.ReadOnlyMap;
+import org.apache.velocity.velosurf.sql.RowHandler;
+import org.apache.velocity.velosurf.sql.SqlUtil;
+import org.apache.velocity.velosurf.util.Logger;
+
+//import org.apache.velocity.velosurf.util.UserContext;
+
+/**
+ * This class is a context wrapper for ResultSets, and provides an iteration mecanism for #foreach loops, as long as getters for values of the current row.
+ *
+ * @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ */
+public class RowIterator implements Iterator<Instance>, RowHandler
+{
+ /**
+ * Build a new RowIterator.
+ *
+ * @param pooledStatement the sql statement
+ * @param resultSet the resultset
+ * @param resultEntity the resulting entity (may be null)
+ */
+ public RowIterator(PooledStatement pooledStatement, ResultSet resultSet, Entity resultEntity)
+ {
+ this.pooledStatement = pooledStatement;
+ this.resultSet = resultSet;
+ this.resultEntity = resultEntity;
+ }
+
+ /**
+ * Returns true if the iteration has more elements.
+ *
+ * @return <code>true</code> if the iterator has more elements.
+ */
+ public boolean hasNext()
+ {
+ boolean ret = false;
+
+ try
+ {
+ /* always need to prefetch, as some JDBC drivers (like HSQLDB driver) seem buggued to this regard */
+ if(isOver)
+ {
+ return false;
+ }
+ else if(prefetch)
+ {
+ return true;
+ }
+ else
+ {
+ try
+ {
+ pooledStatement.getConnection().enterBusyState();
+ ret = resultSet.next();
+ }
+ finally
+ {
+ pooledStatement.getConnection().leaveBusyState();
+ }
+ if(ret)
+ {
+ prefetch = true;
+ }
+ else
+ {
+ isOver = true;
+ pooledStatement.notifyOver();
+ }
+ }
+ return ret;
+ }
+ catch(SQLException e)
+ {
+ Logger.log(e);
+ isOver = true;
+ pooledStatement.notifyOver();
+ return false;
+ }
+ }
+
+ /**
+ * Returns the next element in the iteration.
+ *
+ * @return an Instance.
+ */
+ public Instance next()
+ {
+ try
+ {
+ if(isOver ||!prefetch &&!resultSet.next())
+ {
+ if(!isOver)
+ {
+ isOver = true;
+ pooledStatement.notifyOver();
+ }
+ return null;
+ }
+ prefetch = false;
+ if(resultEntity != null && !resultEntity.isRootEntity())
+ {
+ Instance row = null;
+
+ row = resultEntity.newInstance(new ReadOnlyMap(this), true);
+ row.setClean();
+ return row;
+ }
+ else
+ {
+ return new Instance(new ReadOnlyMap(this),resultEntity == null ? null : resultEntity.getDB());
+ }
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ isOver = true;
+ pooledStatement.notifyOver();
+ return null;
+ }
+ }
+
+ // for Iterator interface, but RO (why? -> positionned updates and deletes => TODO)
+
+ /**
+ * not implemented.
+ */
+ public void remove()
+ {
+ Logger.warn("'remove' not implemented");
+ }
+
+ /**
+ * Generic getter for values of the current row. If no column corresponds to the specified name and a resulting entity has been specified, search among this entity's attributes.
+ * Note that this method is the only getter of RowIterator that cares about obfuscation - other specialized getters
+ * won't do any obfuscation.
+ *
+ * @param key the name of an existing column or attribute
+ * @return an entity, an attribute reference, an instance, a string or null
+ */
+ public Object get(Object key)
+ {
+ String property = (String)key;
+ Object result = null;
+
+ try
+ {
+ if(!dataAvailable())
+ {
+ return null;
+ }
+ if(resultEntity != null && !resultEntity.isRootEntity())
+ {
+ property = resultEntity.resolveName(property);
+
+ Attribute attribute = resultEntity.getAttribute(property);
+
+ if(attribute != null)
+ {
+ switch(attribute.getType())
+ {
+ case Attribute.ROWSET :
+ result = attribute.query(new ReadOnlyMap(this));
+ break;
+ case Attribute.ROW :
+ result = attribute.fetch(new ReadOnlyMap(this));
+ break;
+ case Attribute.SCALAR :
+ result = attribute.evaluate(new ReadOnlyMap(this));
+ break;
+ default :
+ Logger.error("Unknown attribute type for " + resultEntity.getName() + "." + property + "!");
+ }
+ }
+ }
+ if(result == null)
+ {
+ if(resultEntity != null && resultEntity.isObfuscated(property))
+ {
+ result = resultEntity.obfuscate(resultSet.getObject(property));
+ }
+ else
+ {
+ result = resultSet.getObject(property);
+ }
+ }
+ }
+ catch(SQLException e)
+ {
+ Logger.log(e);
+ }
+ return result;
+ }
+
+ /**
+ * Gets all the rows in a list of instances.
+ *
+ * @return a list of all the rows
+ */
+ public List<Instance> getRows()
+ {
+ try
+ {
+ List<Instance> ret = new ArrayList<Instance>();
+
+ pooledStatement.getConnection().enterBusyState();
+ if(resultEntity != null && !resultEntity.isRootEntity())
+ {
+ while(!resultSet.isAfterLast() && resultSet.next())
+ {
+ Instance i = resultEntity.newInstance(new ReadOnlyMap(this), true);
+ i.setClean();
+ ret.add(i);
+ }
+ }
+ else
+ {
+ while(!resultSet.isAfterLast() && resultSet.next())
+ {
+ Instance i = new Instance(new ReadOnlyMap(this), resultEntity == null ? null : resultEntity.getDB());
+ ret.add(i);
+ }
+ }
+ return ret;
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ return null;
+ }
+ finally
+ {
+ pooledStatement.getConnection().leaveBusyState();
+ pooledStatement.notifyOver();
+ isOver = true;
+ }
+ }
+
+ public List getScalars()
+ {
+ try
+ {
+ List ret = new ArrayList();
+
+ pooledStatement.getConnection().enterBusyState();
+ while(!resultSet.isAfterLast() && resultSet.next())
+ {
+ ret.add(resultSet.getObject(0));
+ }
+ return ret;
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ return null;
+ }
+ finally
+ {
+ pooledStatement.getConnection().leaveBusyState();
+ pooledStatement.notifyOver();
+ isOver = true;
+ }
+ }
+
+ Set cachedSet = null;
+
+ /* */
+ public Set<String> keySet()
+ {
+ try
+ {
+ if(cachedSet == null)
+ {
+ cachedSet = new HashSet<String>(SqlUtil.getColumnNames(resultSet));
+ }
+ return cachedSet;
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ return null;
+ }
+ }
+
+ /* */
+ public List<String> keyList()
+ {
+ try
+ {
+ return SqlUtil.getColumnNames(resultSet);
+ }
+ catch(SQLException sqle)
+ {
+ Logger.log(sqle);
+ return null;
+ }
+ }
+
+ /**
+ * Check if some data is available.
+ *
+ * @exception SQLException if the internal ResultSet is not happy
+ * @return <code>true</code> if some data is available (ie the internal
+ * ResultSet is not empty, and not before first row neither after last
+ * one)
+ */
+ private boolean dataAvailable() throws SQLException
+ {
+ boolean ret = false;
+
+ if(resultSet.isBeforeFirst())
+ {
+ try
+ {
+ pooledStatement.getConnection().enterBusyState();
+ ret = resultSet.next();
+ return ret;
+ }
+ finally
+ {
+ pooledStatement.getConnection().leaveBusyState();
+ if(!ret)
+ {
+ pooledStatement.notifyOver();
+ isOver = true;
+ }
+ }
+ }
+ ret = !resultSet.isAfterLast();
+ return ret;
+ }
+
+ /**
+ * Source statement.
+ */
+ private PooledStatement pooledStatement = null;
+
+ /**
+ * Wrapped result set.
+ */
+ private ResultSet resultSet = null;
+
+ /**
+ * Resulting entity.
+ */
+ private Entity resultEntity = null;
+
+ /** whether we did prefetch a row */
+ private boolean prefetch = false;
+
+ /** whether we reached the end */
+ private boolean isOver = false;
+}
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/package.html
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/package.html?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/package.html (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/context/package.html Fri Mar 9 16:23:25 2012
@@ -0,0 +1,23 @@
+<html>
+<body bgcolor="white">
+
+Contains all classes that can be accessed from inside a Velocity template.
+
+<h2>Package Specification</h2>
+
+<!-- ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT GO THERE
+<ul>
+ <li><a href="">##### REFER TO ANY FRAMEMAKER SPECIFICATION HERE #####</a>
+</ul>
+
+<h2>Related Documentation</h2>
+
+For overviews, tutorials, examples, guides, and tool documentation, please see:
+<ul>
+ <li><a href="">##### REFER TO NON-SPEC DOCUMENTATION HERE #####</a>
+</ul>
+
+<!-- Put @see and @since tags down here. -->
+
+</body>
+</html>