001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.util.template;
018:
019: import java.lang.ref.WeakReference;
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: import org.apache.wicket.Application;
024: import org.apache.wicket.Resource;
025: import org.apache.wicket.ResourceReference;
026: import org.apache.wicket.SharedResources;
027: import org.apache.wicket.util.resource.AbstractStringResourceStream;
028: import org.apache.wicket.util.resource.IResourceStream;
029:
030: /**
031: * A resource (reference) factory that takes a TextTemplate and generates shared
032: * resources for various interpolations of that template.
033: * <p>
034: * A scope for adding shared resources allows you to limit the namespace impact
035: * of the shared resources created. If you omit the scope, the application-wide
036: * scope Application.class will be used by default.
037: * <p>
038: * You may use resources created by this factory directly by calling
039: * resourceReference(Map) to get a resource reference to the given shared
040: * resource interpolation represented by the variables in the map. Or, for
041: * convenience, you can use TextTemplateLink to link to resources created by
042: * this factory.
043: * <p>
044: * In many cases, it will be useful to extend this class and override
045: * sharedResourceName(Map) to provide a unique name for resources created by the
046: * factory using map values. If you don't provide an override, every value in
047: * the map will be used to produce the unique name, which may create either
048: * longer names or more unique shared resources than you really wanted.
049: *
050: * @author Jonathan Locke
051: */
052: // TODO Should weak-ref regenerable resources like this in SharedResources!
053: public class TextTemplateSharedResourceFactory {
054: private static final long serialVersionUID = 1L;
055:
056: /**
057: * Shared resource scope
058: */
059: private final WeakReference/*<Class>*/scopeRef;
060:
061: /**
062: * Template to use to create resources
063: */
064: private final TextTemplate template;
065:
066: /**
067: * Creates shared text template resources.
068: *
069: * @param template
070: * The template to interpolate into
071: */
072: public TextTemplateSharedResourceFactory(final TextTemplate template) {
073: this (template, Application.class);
074: }
075:
076: /**
077: * Creates shared text template resources with the given scope.
078: *
079: * @param template
080: * The template to interpolate into
081: * @param scope
082: * The scope in shared resources to add resources at
083: */
084: public TextTemplateSharedResourceFactory(
085: final TextTemplate template, final Class scope) {
086: this .template = template;
087: this .scopeRef = new WeakReference(scope);
088: }
089:
090: /**
091: * @param variables
092: * The variables to interpolate into the template
093: * @return A resource reference to the template encoded as a resource with
094: * the given variables interpolated.
095: */
096: public ResourceReference resourceReference(final Map variables) {
097: final String uniqueName = sharedResourceName(variables);
098: final String templateValue = template.asString(variables);
099: final SharedResources sharedResources = Application.get()
100: .getSharedResources();
101: final Resource resource = sharedResources.get(uniqueName);
102: if (resource == null) {
103: final Resource newResource = new Resource() {
104: private static final long serialVersionUID = 1L;
105:
106: /**
107: * @see org.apache.wicket.Resource#getResourceStream()
108: */
109: public IResourceStream getResourceStream() {
110: return new AbstractStringResourceStream() {
111: private static final long serialVersionUID = 1L;
112:
113: protected String getString() {
114: return templateValue;
115: }
116:
117: public long length() {
118: return templateValue.length();
119: }
120: };
121: }
122: };
123: sharedResources.add(uniqueName, newResource);
124: }
125: return new ResourceReference((Class) scopeRef.get(), uniqueName);
126: }
127:
128: /**
129: * @param variables
130: * Variables that parameterize the linked-to resource
131: * @return A unique name for the variables to use as a resource key
132: */
133: protected String sharedResourceName(final Map variables) {
134: final StringBuffer buffer = new StringBuffer();
135: for (final Iterator iterator = variables.values().iterator(); iterator
136: .hasNext();) {
137: final String value = iterator.next().toString();
138: buffer.append(encodeValue(value));
139: if (iterator.hasNext()) {
140: buffer.append('-');
141: }
142: }
143: return buffer.toString();
144: }
145:
146: /**
147: * Simple encoder for key values. Letters and digits are unchanged. All
148: * others are encoded as %<hexcode>.
149: *
150: * @param value
151: * The value
152: * @return The encoded value
153: */
154: private String encodeValue(final String value) {
155: final StringBuffer buffer = new StringBuffer(
156: value.length() + 10);
157: for (int i = 0; i < value.length(); i++) {
158: final char c = value.charAt(i);
159: if (Character.isLetterOrDigit(c)) {
160: buffer.append(c);
161: } else {
162: buffer.append('%');
163: buffer.append(Integer.toHexString(c));
164: }
165: }
166: return buffer.toString();
167: }
168: }
|