001 // Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.internal.services;
016
017 import org.apache.tapestry5.Binding;
018 import org.apache.tapestry5.BindingConstants;
019 import org.apache.tapestry5.ComponentResources;
020 import org.apache.tapestry5.MarkupWriter;
021 import org.apache.tapestry5.internal.InternalConstants;
022 import org.apache.tapestry5.internal.parser.AttributeToken;
023 import org.apache.tapestry5.internal.parser.ExpansionToken;
024 import org.apache.tapestry5.internal.structure.ExpansionPageElement;
025 import org.apache.tapestry5.ioc.Location;
026 import org.apache.tapestry5.ioc.internal.util.TapestryException;
027 import org.apache.tapestry5.ioc.services.TypeCoercer;
028 import org.apache.tapestry5.runtime.RenderCommand;
029 import org.apache.tapestry5.runtime.RenderQueue;
030 import org.apache.tapestry5.services.BindingSource;
031
032 import java.util.List;
033
034 import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
035
036 public class PageElementFactoryImpl implements PageElementFactory
037 {
038 private final TypeCoercer typeCoercer;
039
040 private final BindingSource bindingSource;
041
042 private static class LiteralStringProvider implements StringProvider
043 {
044 private final String string;
045
046 LiteralStringProvider(String string)
047 {
048 this.string = string;
049 }
050
051 public String provideString()
052 {
053 return string;
054 }
055 }
056
057 public PageElementFactoryImpl(TypeCoercer typeCoercer, BindingSource bindingSource)
058 {
059 this.typeCoercer = typeCoercer;
060 this.bindingSource = bindingSource;
061 }
062
063 public RenderCommand newAttributeElement(ComponentResources componentResources, final AttributeToken token)
064 {
065 final StringProvider provider = parseAttributeExpansionExpression(token.value, componentResources,
066 token.getLocation());
067
068 return new RenderCommand()
069 {
070 public void render(MarkupWriter writer, RenderQueue queue)
071 {
072 writer.attributeNS(token.namespaceURI, token.name, provider.provideString());
073 }
074
075 public String toString()
076 {
077 return String.format("AttributeNS[%s %s \"%s\"]", token.namespaceURI, token.name, token.value);
078 }
079 };
080 }
081
082 private StringProvider parseAttributeExpansionExpression(String expression, ComponentResources resources,
083 final Location location)
084 {
085 final List<StringProvider> providers = newList();
086
087 int startx = 0;
088
089 while (true)
090 {
091 int expansionx = expression.indexOf(InternalConstants.EXPANSION_START, startx);
092
093 // No more expansions, add in the rest of the string as a literal.
094
095 if (expansionx < 0)
096 {
097 if (startx < expression.length())
098 providers.add(new LiteralStringProvider(expression.substring(startx)));
099 break;
100 }
101
102 // Add in a literal string chunk for the characters between the last expansion and
103 // this expansion.
104
105 if (startx != expansionx)
106 providers.add(new LiteralStringProvider(expression.substring(startx, expansionx)));
107
108 int endx = expression.indexOf("}", expansionx);
109
110 if (endx < 0) throw new TapestryException(ServicesMessages
111 .unclosedAttributeExpression(expression), location, null);
112
113 String expansion = expression.substring(expansionx + 2, endx);
114
115 final Binding binding = bindingSource.newBinding("attribute expansion", resources, resources,
116 BindingConstants.PROP, expansion, location);
117
118 final StringProvider provider = new StringProvider()
119 {
120 public String provideString()
121 {
122 try
123 {
124 Object raw = binding.get();
125
126 return typeCoercer.coerce(raw, String.class);
127 } catch (Exception ex)
128 {
129 throw new TapestryException(ex.getMessage(), location, ex);
130 }
131 }
132 };
133
134 providers.add(provider);
135
136 // Restart the search after '}'
137
138 startx = endx + 1;
139 }
140
141 // Simplify the typical case, where the entire attribute is just a single expansion:
142
143 if (providers.size() == 1) return providers.get(0);
144
145 return new StringProvider()
146 {
147
148 public String provideString()
149 {
150 StringBuilder builder = new StringBuilder();
151
152 for (StringProvider provider : providers)
153 builder.append(provider.provideString());
154
155 return builder.toString();
156 }
157 };
158 }
159
160 public RenderCommand newExpansionElement(ComponentResources componentResources, ExpansionToken token)
161 {
162 Binding binding = bindingSource.newBinding("expansion", componentResources, componentResources,
163 BindingConstants.PROP, token.getExpression(), token.getLocation());
164
165 return new ExpansionPageElement(binding, typeCoercer);
166 }
167
168 public Binding newBinding(String parameterName, ComponentResources loadingComponentResources,
169 ComponentResources embeddedComponentResources, String defaultBindingPrefix,
170 String expression, Location location)
171 {
172
173 if (expression.contains(InternalConstants.EXPANSION_START))
174 {
175 StringProvider provider = parseAttributeExpansionExpression(expression, loadingComponentResources,
176 location);
177
178 return new AttributeExpansionBinding(location, provider);
179 }
180
181 return bindingSource.newBinding(parameterName, loadingComponentResources,
182 embeddedComponentResources, defaultBindingPrefix, expression, location);
183 }
184 }