001 // Copyright 2004, 2005 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.tapestry.contrib.inspector;
016
017 import java.util.Iterator;
018 import java.util.Map;
019
020 import org.apache.tapestry.BaseComponent;
021 import org.apache.tapestry.IComponent;
022 import org.apache.tapestry.IDirect;
023 import org.apache.tapestry.IMarkupWriter;
024 import org.apache.tapestry.IRender;
025 import org.apache.tapestry.IRequestCycle;
026 import org.apache.tapestry.engine.DirectServiceParameter;
027 import org.apache.tapestry.engine.IEngineService;
028 import org.apache.tapestry.engine.ILink;
029 import org.apache.tapestry.parse.CloseToken;
030 import org.apache.tapestry.parse.ComponentTemplate;
031 import org.apache.tapestry.parse.LocalizationToken;
032 import org.apache.tapestry.parse.OpenToken;
033 import org.apache.tapestry.parse.TemplateToken;
034 import org.apache.tapestry.parse.TextToken;
035 import org.apache.tapestry.parse.TokenType;
036 import org.apache.tapestry.services.TemplateSource;
037
038 /**
039 * Component of the {@link Inspector}page used to display the ids and types of all embedded
040 * components.
041 *
042 * @author Howard Lewis Ship
043 */
044
045 public abstract class ShowTemplate extends BaseComponent implements IDirect
046 {
047 /** @since 4.0 */
048 public abstract TemplateSource getTemplateSource();
049
050 /** @since 4.1 */
051 public abstract IEngineService getDirectService();
052
053 public boolean getHasTemplate()
054 {
055 Inspector inspector;
056
057 inspector = (Inspector) getPage();
058
059 // Components that inherit from BaseComponent have templates,
060 // others do not.
061
062 return inspector.getInspectedComponent() instanceof BaseComponent;
063 }
064
065 public IRender getTemplateDelegate()
066 {
067 return new IRender()
068 {
069 public void render(IMarkupWriter writer, IRequestCycle cycle)
070 {
071 writeTemplate(writer, cycle);
072 }
073 };
074 }
075
076 /**
077 * Writes the HTML template for the component. When <jwc> tags are written, the id is made
078 * a link (that selects the named component). We use some magic to accomplish this, creating
079 * links as if we were a {@link DirectLink} component, and attributing those links to the
080 * captive {@link DirectLink} component embedded here.
081 */
082
083 private void writeTemplate(IMarkupWriter writer, IRequestCycle cycle)
084 {
085 IComponent inspectedComponent = getInspectedComponent();
086 ComponentTemplate template = null;
087
088 try
089 {
090 template = getTemplateSource().getTemplate(cycle, inspectedComponent);
091 }
092 catch (Exception ex)
093 {
094 return;
095 }
096
097 writer.begin("pre");
098
099 int count = template.getTokenCount();
100
101 for (int i = 0; i < count; i++)
102 {
103 TemplateToken token = template.getToken(i);
104 TokenType type = token.getType();
105
106 if (type == TokenType.TEXT)
107 {
108 write(writer, (TextToken) token);
109 continue;
110 }
111
112 if (type == TokenType.CLOSE)
113 {
114 write(writer, (CloseToken) token);
115
116 continue;
117 }
118
119 if (token.getType() == TokenType.LOCALIZATION)
120 {
121
122 write(writer, (LocalizationToken) token);
123 continue;
124 }
125
126 if (token.getType() == TokenType.OPEN)
127 {
128 boolean nextIsClose = (i + 1 < count)
129 && (template.getToken(i + 1).getType() == TokenType.CLOSE);
130
131 write(writer, nextIsClose, (OpenToken) token);
132
133 if (nextIsClose)
134 i++;
135
136 continue;
137 }
138
139 // That's all the types known at this time.
140 }
141
142 writer.end(); // <pre>
143 }
144
145 /** @since 3.0 * */
146
147 private IComponent getInspectedComponent()
148 {
149 Inspector page = (Inspector) getPage();
150
151 return page.getInspectedComponent();
152 }
153
154 /** @since 3.0 * */
155
156 private void write(IMarkupWriter writer, TextToken token)
157 {
158 // Print the section of the template ... print() will
159 // escape and invalid characters as HTML entities. Also,
160 // we show the full stretch of text, not the trimmed version.
161
162 writer.print(token.getTemplateDataAsString());
163 }
164
165 /** @since 3.0 * */
166
167 private void write(IMarkupWriter writer, CloseToken token)
168 {
169 writer.begin("span");
170 writer.attribute("class", "jwc-tag");
171
172 writer.print("</");
173 writer.print(token.getTag());
174 writer.print(">");
175
176 writer.end(); // <span>
177 }
178
179 /** @since 3.0 * */
180
181 private void write(IMarkupWriter writer, LocalizationToken token)
182 {
183 IComponent component = getInspectedComponent();
184
185 writer.begin("span");
186 writer.attribute("class", "jwc-tag");
187
188 writer.print("<span key=\"");
189 writer.print(token.getKey());
190 writer.print('"');
191
192 Map attributes = token.getAttributes();
193 if (attributes != null && !attributes.isEmpty())
194 {
195 Iterator it = attributes.entrySet().iterator();
196 while (it.hasNext())
197 {
198 Map.Entry entry = (Map.Entry) it.next();
199 String attributeName = (String) entry.getKey();
200 String attributeValue = (String) entry.getValue();
201
202 writer.print(' ');
203 writer.print(attributeName);
204 writer.print("=\"");
205 writer.print(attributeValue);
206 writer.print('"');
207
208 }
209 }
210
211 writer.print('>');
212 writer.begin("span");
213 writer.attribute("class", "localized-string");
214
215 writer.print(component.getMessages().getMessage(token.getKey()));
216 writer.end(); // <span>
217
218 writer.print("</span>");
219
220 writer.end(); // <span>
221 }
222
223 /** @since 3.0 * */
224
225 private void write(IMarkupWriter writer, boolean nextIsClose, OpenToken token)
226 {
227 IComponent component = getInspectedComponent();
228 IEngineService service = getDirectService();
229
230 // Each id references a component embedded in the inspected component.
231 // Get that component.
232
233 String id = token.getId();
234 IComponent embedded = component.getComponent(id);
235 Object[] serviceParameters = new Object[]
236 { embedded.getIdPath() };
237
238 // Build a URL to select that component, as if by the captive
239 // component itself (it's a Direct).
240
241 DirectServiceParameter dsp = new DirectServiceParameter(this, serviceParameters);
242 ILink link = service.getLink(false, dsp);
243
244 writer.begin("span");
245 writer.attribute("class", "jwc-tag");
246
247 writer.print("<");
248 writer.print(token.getTag());
249
250 writer.print(" jwcid=\"");
251
252 writer.begin("span");
253 writer.attribute("class", "jwc-id");
254
255 writer.begin("a");
256 writer.attribute("href", link.getURL());
257 writer.print(id);
258
259 writer.end(); // <a>
260 writer.end(); // <span>
261 writer.print('"');
262
263 Map attributes = token.getAttributesMap();
264
265 if (attributes != null)
266 {
267 Iterator ii = attributes.entrySet().iterator();
268
269 while (ii.hasNext())
270 {
271 Map.Entry e = (Map.Entry) ii.next();
272
273 String value = (String) e.getValue();
274
275 writer.print(' ');
276 writer.print(e.getKey().toString());
277 writer.print("=\"");
278 writer.print(value);
279 writer.print('"');
280 }
281 }
282
283 // Collapse an open & close down to a single tag.
284
285 if (nextIsClose)
286 writer.print('/');
287
288 writer.print('>');
289 writer.end(); // <span>
290 }
291
292 /**
293 * Invoked when a component id is clicked.
294 */
295
296 public void trigger(IRequestCycle cycle)
297 {
298 Inspector inspector = (Inspector) getPage();
299
300 String componentId = (String) cycle.getListenerParameters()[0];
301 inspector.selectComponent(componentId);
302
303 IComponent newComponent = inspector.getInspectedComponent();
304
305 // If the component is not a BaseComponent then it won't have
306 // a template, so switch to the specification view.
307
308 if (!(newComponent instanceof BaseComponent))
309 inspector.setView(View.SPECIFICATION);
310 }
311
312 /**
313 * Always returns true.
314 *
315 * @since 2.3
316 */
317
318 public boolean isStateful()
319 {
320 return true;
321 }
322 }