View Javadoc
1   package org.apache.maven.doxia.macro.snippet;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.BufferedReader;
23  import java.io.IOException;
24  import java.io.InputStreamReader;
25  import java.net.URL;
26  import java.util.ArrayList;
27  import java.util.List;
28  import java.util.regex.Pattern;
29  
30  import org.codehaus.plexus.util.StringUtils;
31  import org.codehaus.plexus.util.IOUtil;
32  
33  /**
34   * Utility class for reading snippets.
35   *
36   * @version $Id$
37   */
38  public class SnippetReader
39  {
40      /** System-dependent EOL. */
41      private static final String EOL = System.getProperty( "line.separator" );
42  
43      /** The source. */
44      private URL source;
45  
46      /** The encoding of the source. */
47      private String encoding;
48  
49      /**
50       * Constructor.
51       *
52       * @param src The source
53       * @param encoding The file encoding
54       */
55      public SnippetReader( URL src, String encoding )
56      {
57          this.source = src;
58          this.encoding = encoding;
59      }
60  
61      /**
62       * Constructor.
63       *
64       * @param src The source
65       */
66      public SnippetReader( URL src )
67      {
68          this( src, null ) ;
69      }
70  
71      /**
72       * Reads the snippet with given id.
73       *
74       * @param snippetId The id of the snippet.
75       * @return The snippet.
76       * @throws java.io.IOException if something goes wrong.
77       */
78      public StringBuffer readSnippet( String snippetId )
79          throws IOException
80      {
81          List<String> lines = readLines( snippetId );
82          int minIndent = minIndent( lines );
83          StringBuffer result = new StringBuffer();
84          for ( String line : lines )
85          {
86              result.append( line.substring( minIndent ) );
87              result.append( EOL );
88          }
89          return result;
90      }
91  
92      /**
93       * Returns the minimal indent of all the lines in the given List.
94       *
95       * @param lines A List of lines.
96       * @return the minimal indent.
97       */
98      int minIndent( List<String> lines )
99      {
100         int minIndent = Integer.MAX_VALUE;
101         for ( String line : lines )
102         {
103             minIndent = Math.min( minIndent, indent( line ) );
104         }
105         return minIndent;
106     }
107 
108     /**
109      * Returns the indent of the given line.
110      *
111      * @param line A line.
112      * @return the indent.
113      */
114     int indent( String line )
115     {
116         char[] chars = line.toCharArray();
117         int indent = 0;
118         for ( ; indent < chars.length; indent++ )
119         {
120             if ( chars[indent] != ' ' )
121             {
122                 break;
123             }
124         }
125         return indent;
126     }
127 
128     /**
129      * Reads the snippet and returns the lines in a List.
130      *
131      * @param snippetId The id of the snippet.
132      * @return A List of lines.
133      * @throws IOException if something goes wrong.
134      */
135     private List<String> readLines( String snippetId )
136         throws IOException
137     {
138         BufferedReader reader;
139         if ( encoding == null || "".equals( encoding ) )
140         {
141             reader = new BufferedReader( new InputStreamReader( source.openStream() ) );
142         }
143         else
144         {
145             reader = new BufferedReader( new InputStreamReader( source.openStream(), encoding ) );
146         }
147 
148         List<String> lines = new ArrayList<String>();
149         try
150         {
151             boolean capture = false;
152             String line;
153             boolean foundStart = false;
154             boolean foundEnd = false;
155             boolean hasSnippetId = StringUtils.isNotEmpty( snippetId );
156             while ( ( line = reader.readLine() ) != null )
157             {
158                 if ( !hasSnippetId )
159                 {
160                     lines.add( line );
161                 }
162                 else
163                 {
164                     if ( isStart( snippetId, line ) )
165                     {
166                         capture = true;
167                         foundStart = true;
168                     }
169                     else if ( isEnd( snippetId, line ) )
170                     {
171                         foundEnd = true;
172                         break;
173                     }
174                     else if ( capture )
175                     {
176                         lines.add( line );
177                     }
178                 }
179             }
180 
181             if ( hasSnippetId && !foundStart )
182             {
183                 throw new IOException( "Failed to find START of snippet " + snippetId + " in file at URL: " + source );
184             }
185             if ( hasSnippetId && !foundEnd )
186             {
187                 throw new IOException( "Failed to find END of snippet " + snippetId + " in file at URL: " + source );
188             }
189         }
190         finally
191         {
192             IOUtil.close( reader );
193         }
194         return lines;
195     }
196 
197     /**
198      * Determines if the given line is a start demarcator.
199      *
200      * @param snippetId the id of the snippet.
201      * @param line the line.
202      * @return True, if the line is a start demarcator.
203      */
204     protected boolean isStart( String snippetId, String line )
205     {
206         return isDemarcator( snippetId, "START", line );
207     }
208 
209     /**
210      * Determines if the given line is a demarcator.
211      *
212      * @param snippetId the id of the snippet.
213      * @param what Identifier for the demarcator.
214      * @param line the line.
215      * @return True, if the line is a start demarcator.
216      */
217     protected static boolean isDemarcator( String snippetId, String what, String line )
218     {
219         // SNIPPET and what are case insensitive
220         // SNIPPET and what can switch order
221         String snippetRegExp = "(^|\\W)(?i:SNIPPET)($|\\W)";
222         String snippetIdRegExp = "(^|\\W)" + snippetId + "($|\\W)";
223         String whatRegExp = "(^|\\W)(?i:" + what + ")($|\\W)";
224         
225         return Pattern.compile( snippetRegExp ).matcher( line ).find()
226             && Pattern.compile( whatRegExp ).matcher( line ).find()
227             && Pattern.compile( snippetIdRegExp ).matcher( line ).find();
228     }
229 
230     /**
231      * Determines if the given line is an end demarcator.
232      *
233      * @param snippetId the id of the snippet.
234      * @param line the line.
235      * @return True, if the line is an end demarcator.
236      */
237     protected boolean isEnd( String snippetId, String line )
238     {
239         return isDemarcator( snippetId, "END", line );
240     }
241 }