1 /*
2 * Copyright (C) Christian Schulte, 2005-206
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 4613 2012-09-22 10:07:08Z 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 4613 2012-09-22 10:07:08Z schulte $
42 *
43 * @see #edit(java.lang.String)
44 */
45 public class LineEditor
46 {
47
48 /** Editor to chain. */
49 private LineEditor editor;
50
51 /** Line separator. */
52 private String lineSeparator;
53
54 /**
55 * Current line number.
56 * @since 1.2
57 */
58 private long lineNumber;
59
60 /** Creates a new {@code LineEditor} instance. */
61 public LineEditor()
62 {
63 this( null, null );
64 }
65
66 /**
67 * Creates a new {@code LineEditor} instance taking a string to use for separating lines.
68 *
69 * @param lineSeparator String to use for separating lines.
70 */
71 public LineEditor( final String lineSeparator )
72 {
73 this( null, lineSeparator );
74 }
75
76 /**
77 * Creates a new {@code LineEditor} instance taking an editor to chain.
78 *
79 * @param editor The editor to chain.
80 */
81 public LineEditor( final LineEditor editor )
82 {
83 this( editor, null );
84 }
85
86 /**
87 * Creates a new {@code LineEditor} instance taking an editor to chain and a string to use for separating lines.
88 *
89 * @param editor The editor to chain.
90 * @param lineSeparator String to use for separating lines.
91 */
92 public LineEditor( final LineEditor editor, final String lineSeparator )
93 {
94 super();
95 this.editor = editor;
96 this.lineSeparator = lineSeparator;
97 this.lineNumber = 0L;
98 }
99
100 /**
101 * Gets the line separator of the editor.
102 *
103 * @return The line separator of the editor.
104 */
105 public final String getLineSeparator()
106 {
107 if ( this.lineSeparator == null )
108 {
109 this.lineSeparator = System.getProperty( "line.separator", "\n" );
110 }
111
112 return this.lineSeparator;
113 }
114
115 /**
116 * Gets the current line number.
117 *
118 * @return The current line number.
119 *
120 * @since 1.2
121 */
122 public final long getLineNumber()
123 {
124 return this.lineNumber;
125 }
126
127 /**
128 * Edits text.
129 * <p>This method splits the given string into lines and passes every line to method {@code editLine} in order of
130 * occurrence. On end of input, method {@code editLine} is called with a {@code null} argument.</p>
131 *
132 * @param text The text to edit or {@code null}.
133 *
134 * @return The edited text or {@code null}.
135 *
136 * @throws IOException if editing fails.
137 */
138 public final String edit( final String text ) throws IOException
139 {
140 String edited = text;
141 this.lineNumber = 0L;
142 BufferedReader reader = null;
143 boolean suppressExceptionOnClose = true;
144
145 try
146 {
147 if ( edited != null )
148 {
149 final StringBuilder buf = new StringBuilder( edited.length() + 16 );
150 boolean appended = false;
151
152 if ( edited.length() > 0 )
153 {
154 reader = new BufferedReader( new StringReader( edited ) );
155
156 String line = null;
157 while ( ( line = reader.readLine() ) != null )
158 {
159 this.lineNumber++;
160 final String replacement = this.editLine( line );
161 if ( replacement != null )
162 {
163 buf.append( replacement ).append( this.getLineSeparator() );
164 appended = true;
165 }
166 }
167 }
168 else
169 {
170 this.lineNumber++;
171 final String replacement = this.editLine( edited );
172 if ( replacement != null )
173 {
174 buf.append( replacement ).append( this.getLineSeparator() );
175 appended = true;
176 }
177 }
178
179 final String replacement = this.editLine( null );
180 if ( replacement != null )
181 {
182 buf.append( replacement );
183 appended = true;
184 }
185
186 edited = appended ? buf.toString() : null;
187 }
188
189 if ( this.editor != null )
190 {
191 edited = this.editor.edit( edited );
192 }
193
194 suppressExceptionOnClose = false;
195 return edited;
196 }
197 finally
198 {
199 try
200 {
201 if ( reader != null )
202 {
203 reader.close();
204 }
205 }
206 catch ( final IOException e )
207 {
208 if ( !suppressExceptionOnClose )
209 {
210 throw e;
211 }
212 }
213 }
214 }
215
216 /**
217 * Edits a line.
218 *
219 * @param line The line to edit or {@code null}, indicating the end of input.
220 *
221 * @return The string to replace {@code line} with or {@code null}, to replace {@code line} with nothing.
222 *
223 * @throws IOException if editing fails.
224 */
225 protected String editLine( final String line ) throws IOException
226 {
227 return line;
228 }
229
230 }