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.Tapestry;
027    import org.apache.tapestry.engine.DirectServiceParameter;
028    import org.apache.tapestry.engine.IEngineService;
029    import org.apache.tapestry.engine.ILink;
030    import org.apache.tapestry.parse.CloseToken;
031    import org.apache.tapestry.parse.ComponentTemplate;
032    import org.apache.tapestry.parse.LocalizationToken;
033    import org.apache.tapestry.parse.OpenToken;
034    import org.apache.tapestry.parse.TemplateToken;
035    import org.apache.tapestry.parse.TextToken;
036    import org.apache.tapestry.parse.TokenType;
037    import 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    
046    public 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    }