001/** 002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC 003 * and GZip files. (http://jwat.org/) 004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/) 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.jwat.common; 019 020import java.io.IOException; 021 022/** 023 * A small class to detect and count occurrences of either LF or CRLF pairs in 024 * a push back input stream, one method each. Implemented as an object instance 025 * so the caller can access the status information. 026 * 027 * A misplaced CR or LF is reported when the character is in the wrong order or 028 * simply not expected. Example LFCR has both CR and LF misplaced. If we are 029 * looking for LF's a CRLF pair has a misplaced CR. 030 * 031 * @author nicl 032 */ 033public class NewlineParser { 034 035 /** Did the reader detect a missing CR while parsing newlines. */ 036 public boolean bMissingCr = false; 037 038 /** Did the reader detect a missing LF while parsing newlines. */ 039 public boolean bMissingLf = false; 040 041 /** Did the reader detect a misplaced CR while parsing newlines. */ 042 public boolean bMisplacedCr = false; 043 044 /** Did the reader detect a misplaced LF while parsing newlines. */ 045 public boolean bMisplacedLf = false; 046 047 /** 048 * Looks forward in the input stream and counts the number of newlines 049 * found. Non newlines characters are pushed back onto the input stream. 050 * @param in data input stream 051 * @param diagnostics object used to report errors and/or warnings 052 * @return newlines found in input stream 053 * @throws IOException if an error occurs while reading data 054 */ 055 public int parseLFs(ByteCountingPushBackInputStream in, Diagnostics diagnostics) throws IOException { 056 bMissingCr = false; 057 bMissingLf = false; 058 bMisplacedCr = false; 059 bMisplacedLf = false; 060 int newlines = 0; 061 byte[] buffer = new byte[2]; 062 boolean bLoop = true; 063 while (bLoop) { 064 int read = in.read(buffer); 065 switch (read) { 066 case 1: 067 if (buffer[0] == '\n') { 068 ++newlines; 069 } else if ((buffer[0] == '\r')) { 070 ++newlines; 071 bMissingLf = true; 072 bMisplacedCr = true; 073 } else { 074 in.unread(buffer[0]); 075 bLoop = false; 076 } 077 break; 078 case 2: 079 if (buffer[0] == '\n') { 080 if (buffer[1] == '\n') { 081 newlines += 2; 082 } else if (buffer[1] != '\r') { 083 ++newlines; 084 in.unread(buffer[1]); 085 } else { 086 ++newlines; 087 bMisplacedCr = true; 088 } 089 } else if (buffer[0] == '\r') { 090 if (buffer[1] == '\n') { 091 ++newlines; 092 bMisplacedCr = true; 093 bMisplacedLf = true; 094 } else { 095 ++newlines; 096 bMisplacedCr = true; 097 bMissingLf = true; 098 in.unread(buffer[1]); 099 } 100 } else { 101 in.unread(buffer); 102 bLoop = false; 103 } 104 break; 105 default: 106 bLoop = false; 107 break; 108 } 109 } 110 if (diagnostics != null) { 111 if (bMissingLf) { 112 diagnostics.addWarning(new Diagnosis(DiagnosisType.ERROR_EXPECTED, 113 "Missing LF", "Sequence of LFs")); 114 } 115 if (bMisplacedCr) { 116 diagnostics.addWarning(new Diagnosis(DiagnosisType.ERROR_EXPECTED, 117 "Misplaced CR", "Sequence of LFs")); 118 } 119 if (bMisplacedLf) { 120 diagnostics.addWarning(new Diagnosis(DiagnosisType.ERROR_EXPECTED, 121 "Misplaced LF", "Sequence of LFs")); 122 } 123 } 124 return newlines; 125 } 126 127 /** 128 * Looks forward in the input stream and counts the number of newlines 129 * found. Non newlines characters are pushed back onto the input stream. 130 * @param in data input stream 131 * @param diagnostics object used to report errors and/or warnings 132 * @return newlines found in input stream 133 * @throws IOException if an error occurs while reading data 134 */ 135 public int parseCRLFs(ByteCountingPushBackInputStream in, Diagnostics diagnostics) throws IOException { 136 bMissingCr = false; 137 bMissingLf = false; 138 bMisplacedCr = false; 139 bMisplacedLf = false; 140 int newlines = 0; 141 byte[] buffer = new byte[2]; 142 boolean bLoop = true; 143 while (bLoop) { 144 int read = in.read(buffer); 145 // Switch on the number of bytes we were able to read (0-2). 146 switch (read) { 147 case 1: 148 if (buffer[0] == '\n') { 149 ++newlines; 150 bMissingCr = true; 151 } else if ((buffer[0] == '\r')) { 152 ++newlines; 153 bMissingLf = true; 154 } else { 155 in.unread(buffer[0]); 156 bLoop = false; 157 } 158 break; 159 case 2: 160 if (buffer[0] == '\r') { 161 if (buffer[1] == '\n') { 162 ++newlines; 163 } else { 164 ++newlines; 165 bMissingLf = true; 166 in.unread(buffer[1]); 167 } 168 } else if (buffer[0] == '\n') { 169 if (buffer[1] == '\r') { 170 ++newlines; 171 bMisplacedCr = true; 172 bMisplacedLf = true; 173 } else { 174 ++newlines; 175 bMissingCr = true; 176 in.unread(buffer[1]); 177 } 178 } else { 179 in.unread(buffer); 180 bLoop = false; 181 } 182 break; 183 default: 184 bLoop = false; 185 break; 186 } 187 } 188 if (diagnostics != null) { 189 if (bMissingCr) { 190 diagnostics.addWarning(new Diagnosis(DiagnosisType.ERROR_EXPECTED, 191 "Missing CR", "Sequence of CRLFs")); 192 } 193 if (bMissingLf) { 194 diagnostics.addWarning(new Diagnosis(DiagnosisType.ERROR_EXPECTED, 195 "Missing LF", "Sequence of CRLFs")); 196 } 197 if (bMisplacedCr) { 198 diagnostics.addWarning(new Diagnosis(DiagnosisType.ERROR_EXPECTED, 199 "Misplaced CR", "Sequence of CRLFs")); 200 } 201 if (bMisplacedLf) { 202 diagnostics.addWarning(new Diagnosis(DiagnosisType.ERROR_EXPECTED, 203 "Misplaced LF", "Sequence of CRLFs")); 204 } 205 } 206 return newlines; 207 } 208 209}