001    /*
002     *   Copyright (C) Christian Schulte, 2005-206
003     *   All rights reserved.
004     *
005     *   Redistribution and use in source and binary forms, with or without
006     *   modification, are permitted provided that the following conditions
007     *   are met:
008     *
009     *     o Redistributions of source code must retain the above copyright
010     *       notice, this list of conditions and the following disclaimer.
011     *
012     *     o Redistributions in binary form must reproduce the above copyright
013     *       notice, this list of conditions and the following disclaimer in
014     *       the documentation and/or other materials provided with the
015     *       distribution.
016     *
017     *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018     *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019     *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020     *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021     *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022     *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023     *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024     *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025     *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026     *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027     *
028     *   $JOMC: LineEditor.java 3875 2011-10-19 09:17:25Z schulte2005 $
029     *
030     */
031    package org.jomc.util;
032    
033    import java.io.BufferedReader;
034    import java.io.IOException;
035    import java.io.StringReader;
036    
037    /**
038     * Interface to line based editing.
039     *
040     * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
041     * @version $JOMC: LineEditor.java 3875 2011-10-19 09:17:25Z schulte2005 $
042     *
043     * @see #edit(java.lang.String)
044     */
045    public class LineEditor
046    {
047    
048        /** Editor to chain. */
049        private LineEditor editor;
050    
051        /** Line separator. */
052        private String lineSeparator;
053    
054        /**
055         * Current line number.
056         * @since 1.2
057         */
058        private long lineNumber;
059    
060        /** Creates a new {@code LineEditor} instance. */
061        public LineEditor()
062        {
063            this( null, null );
064        }
065    
066        /**
067         * Creates a new {@code LineEditor} instance taking a string to use for separating lines.
068         *
069         * @param lineSeparator String to use for separating lines.
070         */
071        public LineEditor( final String lineSeparator )
072        {
073            this( null, lineSeparator );
074        }
075    
076        /**
077         * Creates a new {@code LineEditor} instance taking an editor to chain.
078         *
079         * @param editor The editor to chain.
080         */
081        public LineEditor( final LineEditor editor )
082        {
083            this( editor, null );
084        }
085    
086        /**
087         * Creates a new {@code LineEditor} instance taking an editor to chain and a string to use for separating lines.
088         *
089         * @param editor The editor to chain.
090         * @param lineSeparator String to use for separating lines.
091         */
092        public LineEditor( final LineEditor editor, final String lineSeparator )
093        {
094            super();
095            this.editor = editor;
096            this.lineSeparator = lineSeparator;
097            this.lineNumber = 0L;
098        }
099    
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    }