View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.enforcer.rules.checksum;
20  
21  import java.io.FilterReader;
22  import java.io.IOException;
23  import java.io.Reader;
24  
25  /**
26   * Converts Unix line separators to Windows ones and vice-versa.
27   */
28  class NormalizeLineSeparatorReader extends FilterReader {
29  
30      private static final int EOL = -1;
31  
32      /**
33       * Type representing either Unix or Windows line separators
34       */
35      public enum LineSeparator {
36          WINDOWS("\r\n", null),
37          UNIX("\n", '\r');
38  
39          private final char[] separatorChars;
40  
41          private final Character notPrecededByChar;
42  
43          LineSeparator(String separator, Character notPrecededByChar) {
44              separatorChars = separator.toCharArray();
45              this.notPrecededByChar = notPrecededByChar;
46          }
47  
48          enum MatchResult {
49              NO_MATCH,
50              POTENTIAL_MATCH,
51              MATCH;
52          }
53  
54          /**
55           * Checks if two given characters match the line separator represented by this object.
56           * @param currentCharacter the character to check against
57           * @param previousCharacter optional previous character (may be {@code null})
58           * @return one of {@link MatchResult}
59           */
60          public MatchResult matches(char currentCharacter, Character previousCharacter) {
61              int len = separatorChars.length;
62              if (currentCharacter == separatorChars[len - 1]) {
63                  if (len > 1) {
64                      if (previousCharacter == null || previousCharacter != separatorChars[len - 1]) {
65                          return MatchResult.NO_MATCH;
66                      }
67                  }
68                  if (notPrecededByChar != null) {
69                      if (previousCharacter != null && notPrecededByChar == previousCharacter) {
70                          return MatchResult.NO_MATCH;
71                      }
72                  }
73                  return MatchResult.MATCH;
74              } else if (len > 1 && currentCharacter == separatorChars[len - 2]) {
75                  return MatchResult.POTENTIAL_MATCH;
76              }
77              return MatchResult.NO_MATCH;
78          }
79      }
80  
81      final LineSeparator lineSeparator;
82  
83      Character bufferedCharacter;
84  
85      Character previousCharacter;
86  
87      NormalizeLineSeparatorReader(Reader reader, LineSeparator lineSeparator) {
88          super(reader);
89          this.lineSeparator = lineSeparator;
90          bufferedCharacter = null;
91      }
92  
93      @Override
94      public int read(char[] cbuf, int off, int len) throws IOException {
95          int n;
96          for (n = off; n < off + len; n++) {
97              int readResult = read();
98              if (readResult == EOL) {
99                  return n == 0 ? EOL : n;
100             } else {
101                 cbuf[n] = (char) readResult;
102             }
103         }
104         return n;
105     }
106 
107     @Override
108     public int read() throws IOException {
109         // spool buffered characters, if any
110         if (bufferedCharacter != null) {
111             char localBuffer = bufferedCharacter;
112             bufferedCharacter = null;
113             return localBuffer;
114         }
115         int readResult = super.read();
116         if (readResult == EOL) {
117             return readResult;
118         }
119         char currentCharacter = (char) readResult;
120         if (lineSeparator == LineSeparator.UNIX) {
121             switch (LineSeparator.WINDOWS.matches(currentCharacter, previousCharacter)) {
122                 case MATCH:
123                     return lineSeparator.separatorChars[0];
124                 case POTENTIAL_MATCH:
125                     previousCharacter = currentCharacter;
126                     return read();
127                 default:
128                     // fall-through
129             }
130         } else { // WINDOWS
131             // if current is unix, convert
132             switch (LineSeparator.UNIX.matches(currentCharacter, previousCharacter)) {
133                 case MATCH:
134                     bufferedCharacter = lineSeparator.separatorChars[1];
135                     // set buffered character and return current
136                     return lineSeparator.separatorChars[0];
137                 case POTENTIAL_MATCH:
138                     // invalid option
139                     throw new IllegalStateException("No potential matches expected for Unix line separator");
140                 default:
141                     // fall-through
142             }
143         }
144         previousCharacter = currentCharacter;
145         return currentCharacter;
146     }
147 }