1 /*
2 * Copyright (C) 2005 Christian Schulte <cs@schulte.it>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * o Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * o Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $JOMC: LineEditor.java 5091 2016-04-04 15:40:17Z schulte $
29 *
30 */
31 package org.jomc.util;
32
33 import java.io.BufferedReader;
34 import java.io.IOException;
35 import java.io.StringReader;
36
37 /**
38 * Interface to line based editing.
39 *
40 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
41 * @version $JOMC: LineEditor.java 5091 2016-04-04 15:40:17Z schulte $
42 *
43 * @see #edit(java.lang.String)
44 */
45 public class LineEditor
46 {
47
48 /**
49 * Editor to chain.
50 */
51 private LineEditor editor;
52
53 /**
54 * Line separator.
55 */
56 private String lineSeparator;
57
58 /**
59 * Current line number.
60 *
61 * @since 1.2
62 */
63 private long lineNumber;
64
65 /**
66 * Creates a new {@code LineEditor} instance.
67 */
68 public LineEditor()
69 {
70 this( null, null );
71 }
72
73 /**
74 * Creates a new {@code LineEditor} instance taking a string to use for separating lines.
75 *
76 * @param lineSeparator String to use for separating lines.
77 */
78 public LineEditor( final String lineSeparator )
79 {
80 this( null, lineSeparator );
81 }
82
83 /**
84 * Creates a new {@code LineEditor} instance taking an editor to chain.
85 *
86 * @param editor The editor to chain.
87 */
88 public LineEditor( final LineEditor editor )
89 {
90 this( editor, null );
91 }
92
93 /**
94 * Creates a new {@code LineEditor} instance taking an editor to chain and a string to use for separating lines.
95 *
96 * @param editor The editor to chain.
97 * @param lineSeparator String to use for separating lines.
98 */
99 public LineEditor( final LineEditor editor, final String lineSeparator )
100 {
101 super();
102 this.editor = editor;
103 this.lineSeparator = lineSeparator;
104 this.lineNumber = 0L;
105 }
106
107 /**
108 * Gets the line separator of the editor.
109 *
110 * @return The line separator of the editor.
111 */
112 public final String getLineSeparator()
113 {
114 if ( this.lineSeparator == null )
115 {
116 this.lineSeparator = System.getProperty( "line.separator", "\n" );
117 }
118
119 return this.lineSeparator;
120 }
121
122 /**
123 * Gets the current line number.
124 *
125 * @return The current line number.
126 *
127 * @since 1.2
128 */
129 public final long getLineNumber()
130 {
131 return this.lineNumber;
132 }
133
134 /**
135 * Edits text.
136 * <p>
137 * This method splits the given string into lines and passes every line to method {@code editLine} in order of
138 * occurrence. On end of input, method {@code editLine} is called with a {@code null} argument.
139 * </p>
140 *
141 * @param text The text to edit or {@code null}.
142 *
143 * @return The edited text or {@code null}.
144 *
145 * @throws IOException if editing fails.
146 */
147 public final String edit( final String text ) throws IOException
148 {
149 String edited = text;
150 this.lineNumber = 0L;
151 BufferedReader reader = null;
152 boolean suppressExceptionOnClose = true;
153
154 try
155 {
156 if ( edited != null )
157 {
158 final StringBuilder buf = new StringBuilder( edited.length() + 16 );
159 boolean appended = false;
160
161 if ( edited.length() > 0 )
162 {
163 reader = new BufferedReader( new StringReader( edited ) );
164
165 String line = null;
166 while ( ( line = reader.readLine() ) != null )
167 {
168 this.lineNumber++;
169 final String replacement = this.editLine( line );
170 if ( replacement != null )
171 {
172 buf.append( replacement ).append( this.getLineSeparator() );
173 appended = true;
174 }
175 }
176 }
177 else
178 {
179 this.lineNumber++;
180 final String replacement = this.editLine( edited );
181 if ( replacement != null )
182 {
183 buf.append( replacement ).append( this.getLineSeparator() );
184 appended = true;
185 }
186 }
187
188 final String replacement = this.editLine( null );
189 if ( replacement != null )
190 {
191 buf.append( replacement );
192 appended = true;
193 }
194
195 edited = appended ? buf.toString() : null;
196 }
197
198 if ( this.editor != null )
199 {
200 edited = this.editor.edit( edited );
201 }
202
203 suppressExceptionOnClose = false;
204 return edited;
205 }
206 finally
207 {
208 try
209 {
210 if ( reader != null )
211 {
212 reader.close();
213 }
214 }
215 catch ( final IOException e )
216 {
217 if ( !suppressExceptionOnClose )
218 {
219 throw e;
220 }
221 }
222 }
223 }
224
225 /**
226 * Edits a line.
227 *
228 * @param line The line to edit or {@code null}, indicating the end of input.
229 *
230 * @return The string to replace {@code line} with or {@code null}, to replace {@code line} with nothing.
231 *
232 * @throws IOException if editing fails.
233 */
234 protected String editLine( final String line ) throws IOException
235 {
236 return line;
237 }
238
239 }