001: /*
002: * Copyright (c) 2003 The Visigoth Software Society. All rights
003: * reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * 2. Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowledgement:
019: * "This product includes software developed by the
020: * Visigoth Software Society (http://www.visigoths.org/)."
021: * Alternately, this acknowledgement may appear in the software itself,
022: * if and wherever such third-party acknowledgements normally appear.
023: *
024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
025: * project contributors may be used to endorse or promote products derived
026: * from this software without prior written permission. For written
027: * permission, please contact visigoths@visigoths.org.
028: *
029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
030: * nor may "FreeMarker" or "Visigoth" appear in their names
031: * without prior written permission of the Visigoth Software Society.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of the Visigoth Software Society. For more
049: * information on the Visigoth Software Society, please see
050: * http://www.visigoths.org/
051: */
052:
053: package freemarker.ext.beans;
054:
055: import java.lang.reflect.Field;
056: import java.lang.reflect.Method;
057: import java.lang.reflect.Modifier;
058: import java.util.HashMap;
059: import java.util.Iterator;
060: import java.util.Map;
061:
062: import freemarker.log.Logger;
063: import freemarker.template.TemplateCollectionModel;
064: import freemarker.template.TemplateHashModelEx;
065: import freemarker.template.TemplateModel;
066: import freemarker.template.TemplateModelException;
067:
068: /**
069: * Wraps the static fields and methods of a class in a
070: * {@link freemarker.template.TemplateHashModel}.
071: * Fields are wrapped using {@link BeansWrapper#wrap(Object)}, and
072: * methods are wrapped into an appropriate {@link freemarker.template.TemplateMethodModelEx} instance.
073: * Unfortunately, there is currently no support for bean property-style
074: * calls of static methods, similar to that in {@link BeanModel}.
075: * @author Attila Szegedi
076: * @version $Id: StaticModel.java,v 1.24.2.1 2006/11/12 10:23:02 szegedia Exp $
077: */
078: final class StaticModel implements TemplateHashModelEx {
079: private static final Logger logger = Logger
080: .getLogger("freemarker.beans");
081: private final Class clazz;
082: private final BeansWrapper wrapper;
083: private final Map map = new HashMap();
084:
085: StaticModel(Class clazz, BeansWrapper wrapper)
086: throws TemplateModelException {
087: this .clazz = clazz;
088: this .wrapper = wrapper;
089: populate();
090: }
091:
092: /**
093: * Returns the field or method named by the <tt>key</tt>
094: * parameter.
095: */
096: public TemplateModel get(String key) throws TemplateModelException {
097: Object model = map.get(key);
098: // Simple method, overloaded method or final field -- these have cached
099: // template models
100: if (model instanceof TemplateModel)
101: return (TemplateModel) model;
102: // Non-final field; this must be evaluated on each call.
103: if (model instanceof Field) {
104: try {
105: return wrapper.getOuterIdentity().wrap(
106: ((Field) model).get(null));
107: } catch (IllegalAccessException e) {
108: throw new TemplateModelException(
109: "Illegal access for field " + key
110: + " of class " + clazz.getName());
111: }
112: }
113:
114: throw new TemplateModelException("No such key: " + key
115: + " in class " + clazz.getName());
116: }
117:
118: /**
119: * Returns true if there is at least one public static
120: * field or method in the underlying class.
121: */
122: public boolean isEmpty() {
123: return map.isEmpty();
124: }
125:
126: public int size() {
127: return map.size();
128: }
129:
130: public TemplateCollectionModel keys() throws TemplateModelException {
131: return (TemplateCollectionModel) wrapper.getOuterIdentity()
132: .wrap(map.keySet());
133: }
134:
135: public TemplateCollectionModel values()
136: throws TemplateModelException {
137: return (TemplateCollectionModel) wrapper.getOuterIdentity()
138: .wrap(map.values());
139: }
140:
141: private void populate() throws TemplateModelException {
142: if (!Modifier.isPublic(clazz.getModifiers())) {
143: throw new TemplateModelException(
144: "Can't wrap the non-public class "
145: + clazz.getName());
146: }
147:
148: if (wrapper.getExposureLevel() == BeansWrapper.EXPOSE_NOTHING) {
149: return;
150: }
151:
152: Field[] fields = clazz.getFields();
153: for (int i = 0; i < fields.length; ++i) {
154: Field field = fields[i];
155: int mod = field.getModifiers();
156: if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
157: if (Modifier.isFinal(mod))
158: try {
159: // public static final fields are evaluated once and
160: // stored in the map
161: map.put(field.getName(), wrapper
162: .getOuterIdentity().wrap(
163: field.get(null)));
164: } catch (IllegalAccessException e) {
165: // Intentionally ignored
166: }
167: else
168: // This is a special flagging value: Field in the map means
169: // that this is a non-final field, and it must be evaluated
170: // on each get() call.
171: map.put(field.getName(), field);
172: }
173: }
174: if (wrapper.getExposureLevel() < BeansWrapper.EXPOSE_PROPERTIES_ONLY) {
175: Method[] methods = clazz.getMethods();
176: for (int i = 0; i < methods.length; ++i) {
177: Method method = methods[i];
178: int mod = method.getModifiers();
179: if (Modifier.isPublic(mod) && Modifier.isStatic(mod)
180: && wrapper.isSafeMethod(method)) {
181: String name = method.getName();
182: Object obj = map.get(name);
183: if (obj instanceof Method) {
184: MethodMap methodMap = new MethodMap(name);
185: methodMap.addMethod((Method) obj);
186: methodMap.addMethod(method);
187: map.put(name, methodMap);
188: } else if (obj instanceof MethodMap) {
189: MethodMap methodMap = (MethodMap) obj;
190: methodMap.addMethod(method);
191: } else {
192: if (obj != null) {
193: logger.info("Overwriting value [" + obj
194: + "] for " + " key '" + name
195: + "' with [" + method
196: + "] in static model for "
197: + clazz.getName());
198: }
199: map.put(name, method);
200: }
201: }
202: }
203: for (Iterator entries = map.entrySet().iterator(); entries
204: .hasNext();) {
205: Map.Entry entry = (Map.Entry) entries.next();
206: Object value = entry.getValue();
207: if (value instanceof Method) {
208: Method method = (Method) value;
209: entry.setValue(new SimpleMethodModel(null, method,
210: method.getParameterTypes(), wrapper));
211: } else if (value instanceof MethodMap) {
212: entry.setValue(new OverloadedMethodModel(null,
213: (MethodMap) value, wrapper));
214: }
215: }
216: }
217: }
218: }
|