001    // Copyright 2007, 2008 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.renderers;
016    
017    import org.apache.tapestry5.MarkupWriter;
018    import org.apache.tapestry5.dom.Element;
019    import org.apache.tapestry5.ioc.Location;
020    import org.apache.tapestry5.ioc.Resource;
021    import org.apache.tapestry5.ioc.ScopeConstants;
022    import org.apache.tapestry5.ioc.annotations.Scope;
023    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
024    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
025    import org.apache.tapestry5.services.ObjectRenderer;
026    
027    import java.io.*;
028    import java.util.Set;
029    
030    /**
031     * Responsible for rendering a {@link Location}. It is designed to only perform the full output (which includes a
032     * snippet of the source file) once per render. This requires the use of the "perthread" scope (since the service
033     * tracks, internally, which locations have already been rendered, to avoid repetition).
034     */
035    @Scope(ScopeConstants.PERTHREAD)
036    public class LocationRenderer implements ObjectRenderer<Location>
037    {
038        private static final int RANGE = 5;
039    
040        private final Set<Location> rendered = CollectionFactory.newSet();
041    
042        public void render(Location location, MarkupWriter writer)
043        {
044            writer.write(location.toString());
045    
046            /** If the full details were already rendered this request, then skip the rest. */
047            if (rendered.contains(location)) return;
048    
049            rendered.add(location);
050    
051            Resource r = location.getResource();
052            int line = location.getLine();
053    
054            // No line number? then nothing more to render.
055    
056            if (line <= 0) return;
057    
058            if (!r.exists()) return;
059    
060    
061            int start = line - RANGE;
062            int end = line + RANGE;
063    
064            writer.element("table", "class", "t-location-outer");
065    
066            LineNumberReader reader = null;
067    
068            try
069            {
070                InputStream is = r.openStream();
071                InputStreamReader isr = new InputStreamReader(is);
072                reader = new LineNumberReader(new BufferedReader(isr));
073    
074                while (true)
075                {
076                    String input = reader.readLine();
077    
078                    if (input == null) break;
079    
080                    int current = reader.getLineNumber();
081    
082                    if (current < start) continue;
083    
084                    if (current > end) break;
085    
086                    writer.element("tr");
087    
088                    writer.element("td", "class", "t-location-line");
089    
090                    if (line == current) writer.getElement().addClassName("t-location-current");
091    
092                    writer.write(Integer.toString(current));
093                    writer.end();
094    
095                    Element td = writer.element("td", "class", "t-location-content");
096    
097                    if (line == current) td.addClassName("t-location-current");
098    
099                    if (start == current) td.addClassName("t-location-content-first");
100    
101                    writer.write(input);
102                    writer.end();
103    
104                    writer.end(); // tr
105                }
106    
107                reader.close();
108                reader = null;
109            }
110            catch (IOException ex)
111            {
112                writer.write(ex.toString());
113            }
114            finally
115            {
116                InternalUtils.close(reader);
117            }
118    
119            writer.end(); // div
120        }
121    }