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  public class SnippetReader
37  {
38      /** System-dependent EOL. */
39      private static final String EOL = System.getProperty( "line.separator" );
40  
41      /** The source. */
42      private URL source;
43  
44      /** The encoding of the source. */
45      private String encoding;
46  
47      /**
48       * Constructor.
49       *
50       * @param src The source
51       * @param encoding The file encoding
52       */
53      public SnippetReader( URL src, String encoding )
54      {
55          this.source = src;
56          this.encoding = encoding;
57      }
58  
59      /**
60       * Constructor.
61       *
62       * @param src The source
63       */
64      public SnippetReader( URL src )
65      {
66          this( src, null ) ;
67      }
68  
69      /**
70       * Reads the snippet with given id.
71       *
72       * @param snippetId The id of the snippet.
73       * @return The snippet.
74       * @throws java.io.IOException if something goes wrong.
75       */
76      public StringBuffer readSnippet( String snippetId )
77          throws IOException
78      {
79          List<String> lines = readLines( snippetId );
80          int minIndent = minIndent( lines );
81          StringBuffer result = new StringBuffer();
82          for ( String line : lines )
83          {
84              result.append( line.substring( minIndent ) );
85              result.append( EOL );
86          }
87          return result;
88      }
89  
90      /**
91       * Returns the minimal indent of all the lines in the given List.
92       *
93       * @param lines A List of lines.
94       * @return the minimal indent.
95       */
96      int minIndent( List<String> lines )
97      {
98          int minIndent = Integer.MAX_VALUE;
99          for ( String line : lines )
100         {
101             minIndent = Math.min( minIndent, indent( line ) );
102         }
103         return minIndent;
104     }
105 
106     /**
107      * Returns the indent of the given line.
108      *
109      * @param line A line.
110      * @return the indent.
111      */
112     int indent( String line )
113     {
114         char[] chars = line.toCharArray();
115         int indent = 0;
116         for ( ; indent < chars.length; indent++ )
117         {
118             if ( chars[indent] != ' ' )
119             {
120                 break;
121             }
122         }
123         return indent;
124     }
125 
126     /**
127      * Reads the snippet and returns the lines in a List.
128      *
129      * @param snippetId The id of the snippet.
130      * @return A List of lines.
131      * @throws IOException if something goes wrong.
132      */
133     private List<String> readLines( String snippetId )
134         throws IOException
135     {
136         BufferedReader reader;
137         if ( encoding == null || "".equals( encoding ) )
138         {
139             reader = new BufferedReader( new InputStreamReader( source.openStream() ) );
140         }
141         else
142         {
143             reader = new BufferedReader( new InputStreamReader( source.openStream(), encoding ) );
144         }
145 
146         List<String> lines = new ArrayList<>();
147         try
148         {
149             boolean capture = false;
150             String line;
151             boolean foundStart = false;
152             boolean foundEnd = false;
153             boolean hasSnippetId = StringUtils.isNotEmpty( snippetId );
154             while ( ( line = reader.readLine() ) != null )
155             {
156                 if ( !hasSnippetId )
157                 {
158                     lines.add( line );
159                 }
160                 else
161                 {
162                     if ( isStart( snippetId, line ) )
163                     {
164                         capture = true;
165                         foundStart = true;
166                     }
167                     else if ( isEnd( snippetId, line ) )
168                     {
169                         foundEnd = true;
170                         break;
171                     }
172                     else if ( capture )
173                     {
174                         lines.add( line );
175                     }
176                 }
177             }
178 
179             if ( hasSnippetId && !foundStart )
180             {
181                 throw new IOException( "Failed to find START of snippet " + snippetId + " in file at URL: " + source );
182             }
183             if ( hasSnippetId && !foundEnd )
184             {
185                 throw new IOException( "Failed to find END of snippet " + snippetId + " in file at URL: " + source );
186             }
187         }
188         finally
189         {
190             IOUtil.close( reader );
191         }
192         return lines;
193     }
194 
195     /**
196      * Determines if the given line is a start demarcator.
197      *
198      * @param snippetId the id of the snippet.
199      * @param line the line.
200      * @return True, if the line is a start demarcator.
201      */
202     protected boolean isStart( String snippetId, String line )
203     {
204         return isDemarcator( snippetId, "START", line );
205     }
206 
207     /**
208      * Determines if the given line is a demarcator.
209      *
210      * @param snippetId the id of the snippet.
211      * @param what Identifier for the demarcator.
212      * @param line the line.
213      * @return True, if the line is a start demarcator.
214      */
215     protected static boolean isDemarcator( String snippetId, String what, String line )
216     {
217         // SNIPPET and what are case insensitive
218         // SNIPPET and what can switch order
219         String snippetRegExp = "(^|\\W)(?i:SNIPPET)($|\\W)";
220         String snippetIdRegExp = "(^|\\W)" + snippetId + "($|\\W)";
221         String whatRegExp = "(^|\\W)(?i:" + what + ")($|\\W)";
222         
223         return Pattern.compile( snippetRegExp ).matcher( line ).find()
224             && Pattern.compile( whatRegExp ).matcher( line ).find()
225             && Pattern.compile( snippetIdRegExp ).matcher( line ).find();
226     }
227 
228     /**
229      * Determines if the given line is an end demarcator.
230      *
231      * @param snippetId the id of the snippet.
232      * @param line the line.
233      * @return True, if the line is an end demarcator.
234      */
235     protected boolean isEnd( String snippetId, String line )
236     {
237         return isDemarcator( snippetId, "END", line );
238     }
239 }