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