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.core;
054:
055: import freemarker.template.Template;
056:
057: /**
058: * A class that allows one to associate custom data with a configuration,
059: * a template, or environment. It works pretty much like {@link ThreadLocal}, a
060: * class that allows one to associate custom data with a thread.
061: * @author Attila Szegedi
062: * @version $Id: CustomAttribute.java,v 1.3 2003/11/10 14:05:22 ddekany Exp $
063: */
064: public class CustomAttribute {
065: /**
066: * Constant used in the constructor specifying that this attribute is
067: * scoped by the environment.
068: */
069: public static final int SCOPE_ENVIRONMENT = 0;
070:
071: /**
072: * Constant used in the constructor specifying that this attribute is
073: * scoped by the template.
074: */
075: public static final int SCOPE_TEMPLATE = 1;
076:
077: /**
078: * Constant used in the constructor specifying that this attribute is
079: * scoped by the configuration.
080: */
081: public static final int SCOPE_CONFIGURATION = 2;
082:
083: // We use an internal key instead of 'this' so that malicious subclasses
084: // overriding equals() and hashCode() can't gain access to other attribute
085: // values. That's also the reason why get() and set() are marked final.
086: private final Object key = new Object();
087: private final int scope;
088:
089: /**
090: * Creates a new custom attribute with the specified scope
091: * @param scope one of <tt>SCOPE_</tt> constants.
092: */
093: public CustomAttribute(int scope) {
094: if (scope != SCOPE_ENVIRONMENT && scope != SCOPE_TEMPLATE
095: && scope != SCOPE_CONFIGURATION) {
096: throw new IllegalArgumentException();
097: }
098: this .scope = scope;
099: }
100:
101: /**
102: * This method is invoked when {@link #get()} is invoked without
103: * {@link #set(Object)} being invoked before it to define the value in the
104: * current scope. Override it to create the attribute value on-demand.
105: * @return the initial value for the custom attribute. By default returns null.
106: */
107: protected Object create() {
108: return null;
109: }
110:
111: /**
112: * @return the value of the attribute in the context of the current environment.
113: * @throws IllegalStateException if there is no current environment (and
114: * hence also no current template and configuration), therefore the
115: * attribute's current scope object can't be resolved.
116: */
117: public final Object get() {
118: return getScopeConfigurable().getCustomAttribute(key, this );
119: }
120:
121: /**
122: * @return the value of a template-scope attribute in the context of a
123: * given template.
124: * @throws UnsupportedOperationException if this custom attribute is not a
125: * template-scope attribute
126: * @throws NullPointerException if t is null
127: */
128: public final Object get(Template t) {
129: if (scope != SCOPE_TEMPLATE) {
130: throw new UnsupportedOperationException(
131: "This is not a template-scope attribute");
132: }
133: return ((Configurable) t).getCustomAttribute(key, this );
134: }
135:
136: /**
137: * Sets the value of the attribute in the context of the current environment.
138: * @param value the new value of the attribute
139: * @throws IllegalStateException if there is no current environment (and
140: * hence also no current template and configuration), therefore the
141: * attribute's current scope object can't be resolved.
142: */
143: public final void set(Object value) {
144: getScopeConfigurable().setCustomAttribute(key, value);
145: }
146:
147: /**
148: * Sets the value of a template-scope attribute in the context of the given
149: * template.
150: * @param value the new value of the attribute
151: * @param t the template
152: * @throws UnsupportedOperationException if this custom attribute is not a
153: * template-scope attribute
154: * @throws NullPointerException if t is null
155: */
156: public final void set(Object value, Template t) {
157: if (scope != SCOPE_TEMPLATE) {
158: throw new UnsupportedOperationException(
159: "This is not a template-scope attribute");
160: }
161: ((Configurable) t).setCustomAttribute(key, value);
162: }
163:
164: private Configurable getScopeConfigurable() {
165: Configurable c = Environment.getCurrentEnvironment();
166: if (c == null) {
167: throw new IllegalStateException("No current environment");
168: }
169: switch (scope) {
170: case SCOPE_ENVIRONMENT: {
171: return c;
172: }
173: case SCOPE_TEMPLATE: {
174: return c.getParent();
175: }
176: case SCOPE_CONFIGURATION: {
177: return c.getParent().getParent();
178: }
179: default: {
180: throw new Error();
181: }
182: }
183: }
184: }
|