001package org.apache.maven.doxia.macro.snippet;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.BufferedReader;
023import java.io.IOException;
024import java.io.InputStreamReader;
025import java.net.URL;
026import java.util.ArrayList;
027import java.util.List;
028import java.util.Locale;
029
030import org.codehaus.plexus.util.IOUtil;
031
032/**
033 * Utility class for reading snippets.
034 *
035 * @version $Id$
036 */
037public class SnippetReader
038{
039    /** System-dependent EOL. */
040    private static final String EOL = System.getProperty( "line.separator" );
041
042    /** The source. */
043    private URL source;
044
045    /** The encoding of the source. */
046    private String encoding;
047
048    /**
049     * Constructor.
050     *
051     * @param src The source
052     * @param encoding The file encoding
053     */
054    public SnippetReader( URL src, String encoding )
055    {
056        this.source = src;
057        this.encoding = encoding;
058    }
059
060    /**
061     * Constructor.
062     *
063     * @param src The source
064     */
065    public SnippetReader( URL src )
066    {
067        this( src, null ) ;
068    }
069
070    /**
071     * Reads the snippet with given id.
072     *
073     * @param snippetId The id of the snippet.
074     * @return The snippet.
075     * @throws java.io.IOException if something goes wrong.
076     */
077    public StringBuffer readSnippet( String snippetId )
078        throws IOException
079    {
080        List<String> lines = readLines( snippetId );
081        int minIndent = minIndent( lines );
082        StringBuffer result = new StringBuffer();
083        for ( String line : lines )
084        {
085            result.append( line.substring( minIndent ) );
086            result.append( EOL );
087        }
088        return result;
089    }
090
091    /**
092     * Returns the minimal indent of all the lines in the given List.
093     *
094     * @param lines A List of lines.
095     * @return the minimal indent.
096     */
097    int minIndent( List<String> lines )
098    {
099        int minIndent = Integer.MAX_VALUE;
100        for ( String line : lines )
101        {
102            minIndent = Math.min( minIndent, indent( line ) );
103        }
104        return minIndent;
105    }
106
107    /**
108     * Returns the indent of the given line.
109     *
110     * @param line A line.
111     * @return the indent.
112     */
113    int indent( String line )
114    {
115        char[] chars = line.toCharArray();
116        int indent = 0;
117        for ( ; indent < chars.length; indent++ )
118        {
119            if ( chars[indent] != ' ' )
120            {
121                break;
122            }
123        }
124        return indent;
125    }
126
127    /**
128     * Reads the snippet and returns the lines in a List.
129     *
130     * @param snippetId The id of the snippet.
131     * @return A List of lines.
132     * @throws IOException if something goes wrong.
133     */
134    private List<String> readLines( String snippetId )
135        throws IOException
136    {
137        BufferedReader reader;
138        if ( encoding == null || "".equals( encoding ) )
139        {
140            reader = new BufferedReader( new InputStreamReader( source.openStream() ) );
141        }
142        else
143        {
144            reader = new BufferedReader( new InputStreamReader( source.openStream(), encoding ) );
145        }
146
147        List<String> lines = new ArrayList<String>();
148        try
149        {
150            boolean capture = false;
151            String line;
152            while ( ( line = reader.readLine() ) != null )
153            {
154                if ( snippetId == null || "".equals( snippetId.trim() ) )
155                {
156                    lines.add( line );
157                }
158                else
159                {
160                    if ( isStart( snippetId, line ) )
161                    {
162                        capture = true;
163                    }
164                    else if ( isEnd( snippetId, line ) )
165                    {
166                        break;
167                    }
168                    else if ( capture )
169                    {
170                        lines.add( line );
171                    }
172                }
173            }
174        }
175        finally
176        {
177            IOUtil.close( reader );
178        }
179        return lines;
180    }
181
182    /**
183     * Determines if the given line is a start demarcator.
184     *
185     * @param snippetId the id of the snippet.
186     * @param line the line.
187     * @return True, if the line is a start demarcator.
188     */
189    protected boolean isStart( String snippetId, String line )
190    {
191        return isDemarcator( snippetId, "START", line );
192    }
193
194    /**
195     * Determines if the given line is a demarcator.
196     *
197     * @param snippetId the id of the snippet.
198     * @param what Identifier for the demarcator.
199     * @param line the line.
200     * @return True, if the line is a start demarcator.
201     */
202    protected boolean isDemarcator( String snippetId, String what, String line )
203    {
204        String upper = line.toUpperCase( Locale.ENGLISH );
205        return upper.contains( what.toUpperCase( Locale.ENGLISH ) )
206            && upper.contains( "SNIPPET" )
207            && line.contains( snippetId );
208    }
209
210    /**
211     * Determines if the given line is an end demarcator.
212     *
213     * @param snippetId the id of the snippet.
214     * @param line the line.
215     * @return True, if the line is an end demarcator.
216     */
217    protected boolean isEnd( String snippetId, String line )
218    {
219        return isDemarcator( snippetId, "END", line );
220    }
221}