    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}