001// Copyright 2007, 2008, 2012 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.tapestry5.internal.renderers;
016
017import org.apache.tapestry5.MarkupWriter;
018import org.apache.tapestry5.commons.Location;
019import org.apache.tapestry5.commons.Resource;
020import org.apache.tapestry5.commons.util.CollectionFactory;
021import org.apache.tapestry5.dom.Element;
022import org.apache.tapestry5.ioc.ScopeConstants;
023import org.apache.tapestry5.ioc.annotations.Scope;
024import org.apache.tapestry5.ioc.internal.util.InternalUtils;
025import org.apache.tapestry5.services.ObjectRenderer;
026
027import java.io.*;
028import 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)
036public 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)
091                {
092                    writer.getElement().attribute("class", "t-location-current");
093                }
094
095                writer.write(Integer.toString(current));
096                writer.end();
097
098                Element td = writer.element("td", "class", "t-location-content");
099
100                if (line == current)
101                {
102                    td.attribute("class", "t-location-current");
103                }
104
105                if (start == current)
106                {
107                    td.attribute("class", "t-location-content-first");
108                }
109
110                writer.write(input);
111                writer.end();
112
113                writer.end(); // tr
114            }
115
116            reader.close();
117            reader = null;
118        }
119        catch (IOException ex)
120        {
121            writer.write(ex.toString());
122        }
123        finally
124        {
125            InternalUtils.close(reader);
126        }
127
128        writer.end(); // div
129    }
130}