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 }