View Javadoc

1   /***
2    * Copyright (C) 2008 rweber <quietgenie@users.sourceforge.net>
3    * 
4    * This file is part of CsvObjectMapper.
5    * 
6    * CsvObjectMapper is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU Lesser General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   * 
11   * CsvObjectMapper is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Lesser General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Lesser General Public License
17   * along with CsvObjectMapper.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  /***
21   * 
22   */
23  package com.projectnine.csvmapper;
24  
25  import java.util.List;
26  import java.util.Vector;
27  
28  import org.apache.commons.jexl.JexlContext;
29  import org.apache.commons.jexl.resolver.FlatResolver;
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  
33  /***
34   * A custom variable resolver that performs default variable resolution.
35   * However, when a JEXL expression of the form '#DEFINE [variableName] =
36   * [fully.qualified.class.Name] [constructor,"args"];' this resolver allows JEXL
37   * to instantiate new instances of objects and add them to the context.
38   * 
39   * @author robweber
40   * 
41   */
42  public class PoundDefineJexlResolver extends FlatResolver {
43      private static final Log log = LogFactory
44  	    .getLog(PoundDefineJexlResolver.class);
45  
46      public static final String POUND_DEFINE_EXPRESSION_START = "'#DEFINE";
47      public static final String INTERNAL_QUOTATION_MARK = "|";
48  
49      /*
50       * (non-Javadoc)
51       * 
52       * @see
53       * org.apache.commons.jexl.resolver.FlatResolver#evaluate(org.apache.commons
54       * .jexl.JexlContext, java.lang.String)
55       */
56      @SuppressWarnings("unchecked")
57      @Override
58      public Object evaluate(JexlContext context, String rawExpression) {
59  	if (rawExpression.startsWith(POUND_DEFINE_EXPRESSION_START)) {
60  	    String expression = rawExpression.substring(1, rawExpression
61  		    .length() - 1);
62  	    String[] expressionParts = expression.split(" ");
63  	    try {
64  		String variableName = expressionParts[1];
65  		String className = expressionParts[3];
66  		List<Class<? extends Object>> parameterTypes = new Vector<Class<? extends Object>>();
67  		List<Object> arguments = new Vector<Object>();
68  		if (expressionParts.length >= 4) {
69  		    if (expression.indexOf(INTERNAL_QUOTATION_MARK) != -1
70  			    && expression.lastIndexOf(INTERNAL_QUOTATION_MARK) != expression
71  				    .indexOf(INTERNAL_QUOTATION_MARK)) {
72  			expressionParts = massageStringLiterals(expression);
73  		    }
74  		    for (int i = 4; i < expressionParts.length; i++) {
75  			String argumentReference = expressionParts[i];
76  			if (!argumentReference
77  				.startsWith(INTERNAL_QUOTATION_MARK)) {
78  			    Object argument = context.getVars().get(
79  				    argumentReference);
80  			    log.debug("Adding an argument called "
81  				    + argumentReference + " of type "
82  				    + argument.getClass().getName()
83  				    + " to the argument list.");
84  			    arguments.add(argument);
85  			    parameterTypes.add(argument.getClass());
86  			} else {
87  			    String stringExpression = expressionParts[i]
88  				    .replace("\0", " ");
89  			    String argument = stringExpression.substring(1,
90  				    stringExpression.length() - 1);
91  			    log.debug("Adding the string argument, " + argument
92  				    + ", to the argument list.");
93  			    arguments.add(argument);
94  			    parameterTypes.add(String.class);
95  			}
96  		    }
97  		}
98  
99  		Object o = Class.forName(className).getConstructor(
100 			parameterTypes
101 				.toArray(new Class[parameterTypes.size()]))
102 			.newInstance(arguments.toArray());
103 
104 		context.getVars().put(variableName, o);
105 	    } catch (Throwable t) {
106 		throw new RuntimeException(t);
107 	    }
108 	}
109 	return super.evaluate(context, rawExpression);
110     }
111 
112     private static String[] massageStringLiterals(String rawExpression) {
113 	String expression = rawMassageStringLiterals(rawExpression);
114 
115 	return expression.split(" ");
116     }
117 
118     /***
119      * @param expression
120      * @return
121      */
122     private static String rawMassageStringLiterals(String expression) {
123 	StringBuffer currentStringLiteralBuffer = new StringBuffer();
124 	StringBuffer expressionBuffer = new StringBuffer();
125 	boolean inStringLiteral = false;
126 	for (int i = 0; i < expression.length(); i++) {
127 	    if (String.valueOf(expression.charAt(i)).equals(
128 		    INTERNAL_QUOTATION_MARK)) {
129 		if (inStringLiteral) {
130 		    currentStringLiteralBuffer.append(INTERNAL_QUOTATION_MARK);
131 		    inStringLiteral = false;
132 		    String stringLiteral = currentStringLiteralBuffer
133 			    .toString();
134 		    String massagedStringLiteral = stringLiteral.replace(" ",
135 			    "\0");
136 		    log.debug("Adding the string literal, " + stringLiteral
137 			    + ", to the expression as: "
138 			    + massagedStringLiteral + ".");
139 
140 		    expressionBuffer.append(massagedStringLiteral);
141 		    currentStringLiteralBuffer = new StringBuffer();
142 		} else {
143 		    currentStringLiteralBuffer.append(INTERNAL_QUOTATION_MARK);
144 		    inStringLiteral = true;
145 		}
146 	    } else {
147 		if (inStringLiteral) {
148 		    currentStringLiteralBuffer.append(expression.charAt(i));
149 		} else {
150 		    expressionBuffer.append(expression.charAt(i));
151 		}
152 	    }
153 	}
154 	return expressionBuffer.toString();
155     }
156 
157     public static void main(String[] args) {
158 	System.out
159 		.println(PoundDefineJexlResolver
160 			.rawMassageStringLiterals("'#DEFINE sdfPlusTime = java.text.SimpleDateFormat |MM/dd/yyyy hh:mm a zzz|'"));
161     }
162 
163 }