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    }