Author: ssmiweve
Date: 2009-01-26 23:32:09 +0100 (Mon, 26 Jan 2009)
New Revision: 7165
Added:
branches/2.18/generic.sesam/velocity-directives/src/main/java/no/sesat/search/view/velocity/JsonDataModelDirective.java
Log:
* Convert sections (datamodel.includes) of the context's DataModel into a JSON
object.
* This allows client javascript to use the datamodel exactly like JSP and
velocity templates do.
*
* For example, adding to any tab (in views.xml) the following
* <layout id="json" main="datamodel-json.vm" >
* <property key="datamodel.includes"
value="searches,navigation,query"/>
* <property key="datamodel.includes" value="configuration"/>
* </layout>
*
* allows the javascript to access after a request to this tab with the
addition URL parameter "layout=json"
* to access datamodel.getSearches() datamodel.getNavigation()
datamodel.getQuery()
* but will exclude any bean properties, at any level, named "configuration".
*
* Or what to serialise can be passed into the directive as named argument
pairs.
* For example: <br/>
* #jsonDataModel('yellow' $datamodel.getSearch('yellow').results 'yellow'
$datamodel.getSearch('yellow').results)
*
* Add prettyJson=true to the url to get the output in pretty format.
*
* This class was inspired by Karl ?\195?\152ie's work in MapJSONDirective.java
Added:
branches/2.18/generic.sesam/velocity-directives/src/main/java/no/sesat/search/view/velocity/JsonDataModelDirective.java
===================================================================
---
branches/2.18/generic.sesam/velocity-directives/src/main/java/no/sesat/search/view/velocity/JsonDataModelDirective.java
(rev 0)
+++
branches/2.18/generic.sesam/velocity-directives/src/main/java/no/sesat/search/view/velocity/JsonDataModelDirective.java
2009-01-26 22:32:09 UTC (rev 7165)
@@ -0,0 +1,208 @@
+/* Copyright (2009) Schibsted Søk AS
+ * This file is part of SESAT.
+ *
+ * SESAT is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * SESAT is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with SESAT. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package no.sesat.search.view.velocity;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+import net.sf.json.JSONSerializer;
+import net.sf.json.JsonConfig;
+import net.sf.json.util.CycleDetectionStrategy;
+import no.sesat.search.datamodel.DataModel;
+import no.sesat.search.view.config.SearchTab.Layout;
+import org.apache.log4j.Logger;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.parser.node.Node;
+
+/** Convert sections (datamodel.includes) of the context's DataModel into a
JSON object.
+ * This allows client javascript to use the datamodel exactly like JSP and
velocity templates do.
+ *
+ * <br/><br/>
+ *
+ * For example, adding to any tab (in views.xml) the following
+ * <pre>
+ * <layout id="json" main="datamodel-json.vm" >
+ * <property key="datamodel.includes"
value="searches,navigation,query"/>
+ * <property key="datamodel.includes" value="configuration"/>
+ * </layout>
+ * </pre>
+ *
+ * allows the javascript to access after a request to this tab with the
addition URL parameter "layout=json"
+ * to access datamodel.getSearches() datamodel.getNavigation()
datamodel.getQuery()
+ * but will exclude any bean properties, at any level, named "configuration".
+ *
+ * Or what to serialise can be passed into the directive as named argument
pairs.
+ * For example: <br/>
+ * #jsonDataModel('yellow' $datamodel.getSearch('yellow').results 'yellow'
$datamodel.getSearch('yellow').results)
+ *
+ * <br/><br/> Add prettyJson=true to the url to get the output in pretty
format.
+ *
+ * <br/><br/>This class was inspired by Karl Øie's work in
MapJSONDirective.java
+ *
+ * @version $Id$
+ */
+public final class JsonDataModelDirective extends AbstractDirective {
+
+ // Constants -----------------------------------------------------
+
+ private static final Logger LOG =
Logger.getLogger(JsonDataModelDirective.class);
+
+ private static final String NAME = "jsonDataModel";
+
+ private static final String DATAMODEL_INCLUDES = "datamodel.includes";
+ private static final String DATAMODEL_EXCLUDES = "datamodel.excludes";
+
+ // Attributes ----------------------------------------------------
+
+ // Static --------------------------------------------------------
+
+ // Constructors --------------------------------------------------
+
+ public JsonDataModelDirective() {}
+
+ // Public --------------------------------------------------------
+
+ public String getName() {
+ return NAME;
+ }
+
+ public int getType() {
+ return LINE;
+ }
+
+ public boolean render(
+ final InternalContextAdapter context,
+ final Writer writer,
+ final Node node)
+ throws IOException, ResourceNotFoundException,
ParseErrorException, MethodInvocationException {
+
+ if (0 <= node.jjtGetNumChildren()) {
+
+ final JsonConfig config = new JsonConfig();
+ // Use NOPROP to indicate any cyclic references
+ config.setCycleDetectionStrategy(CycleDetectionStrategy.NOPROP);
+ // Ignore all transient bean properties
+ config.setIgnoreTransientFields(true);
+
+ final String[] excludes = null !=
((Layout)context.get("layout")).getProperty(DATAMODEL_EXCLUDES)
+ ?
((Layout)context.get("layout")).getProperty(DATAMODEL_EXCLUDES).split(",")
+ : new String[0];
+
+ final String[] fullExcludes = new String[excludes.length + 2];
+ // Ignore all of the junkYard (it's deprecated)
+ fullExcludes[0] = "junkYard";
+ // and all toString methods.
+ fullExcludes[1] = "string";
+ System.arraycopy(excludes, 0, fullExcludes, 2, excludes.length);
+ config.setExcludes(fullExcludes);
+
+ final String[] includes = null !=
((Layout)context.get("layout")).getProperty(DATAMODEL_INCLUDES)
+ ?
((Layout)context.get("layout")).getProperty(DATAMODEL_INCLUDES).split(",")
+ : new String[0];
+
+ final DataModel datamodel = getDataModel(context);
+
+ final Map<String,Object> map = new HashMap<String,Object>();
+
+ for(String include : includes){
+
+ try {
+
+ map.put(include, getDataModelInclude(datamodel, include));
+
+ } catch (IntrospectionException ex) {
+ LOG.error("failed to add include " + include, ex);
+ } catch (IllegalAccessException ex) {
+ LOG.error("failed to add include " + include, ex);
+ } catch (IllegalArgumentException ex) {
+ LOG.error("failed to add include " + include, ex);
+ } catch (InvocationTargetException ex) {
+ LOG.error("failed to add include " + include, ex);
+ }
+ }
+
+ for(int i = 0; i + 1 < node.jjtGetNumChildren(); i+=2){
+ map.put(getArgument(context, node, i),
getObjectArgument(context, node, i+1));
+ }
+
+ assert 0 < map.size() : "No datamodel.includes included";
+
+ if(null != datamodel.getParameters().getValue("prettyJson")){
+ writer.write(JSONSerializer.toJSON(map, config).toString(3));
+ }else{
+ JSONSerializer.toJSON(map, config).write(writer);
+ }
+
+
+ }else{
+ LOG.error("#" + getName() + " - wrong number of arguments");
+ return false;
+ }
+
+ return true;
+ }
+
+
+ // Z implementation ----------------------------------------------
+
+ // Y overrides ---------------------------------------------------
+
+ // Package protected ---------------------------------------------
+
+ // Protected -----------------------------------------------------
+
+ // Private -------------------------------------------------------
+
+ private Object getDataModelInclude(final DataModel datamodel, final String
include)
+ throws IntrospectionException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException{
+
+ Object dataObject = datamodel;
+
+ for(int firstDot = 0
+ ; -1 != firstDot
+ ; firstDot = include.indexOf('.', firstDot + 1)) {
+
+ final int secondDot = include.indexOf('.', firstDot + 1);
+
+ final String beanName
+ = include.substring(firstDot, -1 != secondDot ? secondDot
: include.length());
+
+ final BeanInfo beanInfo =
Introspector.getBeanInfo(dataObject.getClass());
+
+ for(PropertyDescriptor prop : beanInfo.getPropertyDescriptors()){
+ if(beanName.equals(prop.getName())){
+ dataObject = prop.getReadMethod().invoke(dataObject);
+ break;
+ }
+ }
+ }
+ return dataObject;
+ }
+
+ // Inner classes -------------------------------------------------
+
+}
Property changes on:
branches/2.18/generic.sesam/velocity-directives/src/main/java/no/sesat/search/view/velocity/JsonDataModelDirective.java
___________________________________________________________________
Added: svn:keywords
+ Id
_______________________________________________
Kernel-commits mailing list
[email protected]
http://sesat.no/mailman/listinfo/kernel-commits