EMMA Coverage Report (generated Tue Jan 19 17:53:40 UTC 2010)
[all classes][org.jomc.util]

COVERAGE SUMMARY FOR SOURCE FILE [SectionEditor.java]

nameclass, %method, %block, %line, %
SectionEditor.java100% (1/1)75%  (9/12)90%  (351/389)88%  (74.8/85)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SectionEditor100% (1/1)75%  (9/12)90%  (351/389)88%  (74.8/85)
SectionEditor (LineEditor): void 0%   (0/1)0%   (0/4)0%   (0/2)
SectionEditor (LineEditor, String): void 0%   (0/1)0%   (0/5)0%   (0/2)
SectionEditor (String): void 0%   (0/1)0%   (0/4)0%   (0/2)
editSection (Section): void 100% (1/1)38%  (3/8)67%  (2/3)
getOutput (Section): String 100% (1/1)72%  (13/18)75%  (3/4)
editSections (Section): void 100% (1/1)81%  (21/26)83%  (5/6)
editLine (String): String 100% (1/1)95%  (201/211)97%  (43.8/45)
SectionEditor (): void 100% (1/1)100% (3/3)100% (2/2)
getMessage (String, Object): String 100% (1/1)100% (14/14)100% (1/1)
getSection (String): Section 100% (1/1)100% (34/34)100% (8/8)
isSectionFinished (String): boolean 100% (1/1)100% (11/11)100% (1/1)
renderSections (Section, StringBuilder): StringBuilder 100% (1/1)100% (51/51)100% (9/9)

1/*
2 *   Copyright (c) 2009 The JOMC Project
3 *   Copyright (c) 2005 Christian Schulte <cs@jomc.org>
4 *   All rights reserved.
5 *
6 *   Redistribution and use in source and binary forms, with or without
7 *   modification, are permitted provided that the following conditions
8 *   are met:
9 *
10 *     o Redistributions of source code must retain the above copyright
11 *       notice, this list of conditions and the following disclaimer.
12 *
13 *     o Redistributions in binary form must reproduce the above copyright
14 *       notice, this list of conditions and the following disclaimer in
15 *       the documentation and/or other materials provided with the
16 *       distribution.
17 *
18 *   THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
19 *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
22 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 *   OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 *   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 *   $Id: SectionEditor.java 891 2009-11-02 03:40:00Z schulte2005 $
31 *
32 */
33package org.jomc.util;
34 
35import java.io.IOException;
36import java.text.MessageFormat;
37import java.util.ResourceBundle;
38import java.util.Stack;
39 
40/**
41 * Interface to section based editing.
42 * <p>Section based editing is a two phase process of parsing the editor's input into a corresponding hierarchy of
43 * {@code Section} instances, followed by rendering the parsed sections to produce the output of the editor. Method
44 * {@code editLine} returns {@code null} during parsing and the output of the editor on end of input, rendered by
45 * calling method {@code getOutput}. Parsing is backed by methods {@code getSection} and {@code isSectionFinished}.</p>
46 *
47 * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
48 * @version $Id: SectionEditor.java 891 2009-11-02 03:40:00Z schulte2005 $
49 *
50 * @see #edit(java.lang.String)
51 */
52public class SectionEditor extends LineEditor
53{
54 
55    /** Marker indicating the start of a section. */
56    private static final String DEFAULT_SECTION_START = "SECTION-START[";
57 
58    /** Marker indicating the end of a section. */
59    private static final String DEFAULT_SECTION_END = "SECTION-END";
60 
61    /** Stack of sections. */
62    private Stack<Section> stack;
63 
64    /** Creates a new {@code SectionEditor} instance. */
65    public SectionEditor()
66    {
67        super();
68    }
69 
70    /**
71     * Creates a new {@code SectionEditor} instance taking a string to use for separating lines.
72     *
73     * @param lineSeparator String to use for separating lines.
74     */
75    public SectionEditor( final String lineSeparator )
76    {
77        super( lineSeparator );
78    }
79 
80    /**
81     * Creates a new {@code SectionEditor} instance taking an editor to chain.
82     *
83     * @param editor The editor to chain.
84     */
85    public SectionEditor( final LineEditor editor )
86    {
87        super( editor );
88    }
89 
90    /**
91     * Creates a new {@code SectionEditor} instance taking an editor to chain and a string to use for separating lines.
92     *
93     * @param editor The editor to chain.
94     * @param lineSeparator String to use for separating lines.
95     */
96    public SectionEditor( final LineEditor editor, final String lineSeparator )
97    {
98        super( editor, lineSeparator );
99    }
100 
101    @Override
102    protected final String editLine( final String line ) throws IOException
103    {
104        if ( this.stack == null )
105        {
106            final Section root = new Section();
107            root.setMode( Section.MODE_HEAD );
108            this.stack = new Stack<Section>();
109            this.stack.push( root );
110        }
111 
112        Section current = this.stack.peek();
113        String replacement = null;
114 
115        if ( line != null )
116        {
117            final Section child = this.getSection( line );
118 
119            if ( child != null )
120            {
121                child.setStartingLine( line );
122                child.setMode( Section.MODE_HEAD );
123 
124                if ( current.getMode() == Section.MODE_TAIL && current.getTailContent().length() > 0 )
125                {
126                    final Section s = new Section();
127                    s.getHeadContent().append( current.getTailContent() );
128                    current.getTailContent().setLength( 0 );
129                    current.getSections().add( s );
130                    current = s;
131                    this.stack.push( current );
132                }
133 
134                current.getSections().add( child );
135                current.setMode( Section.MODE_TAIL );
136                this.stack.push( child );
137            }
138            else if ( this.isSectionFinished( line ) )
139            {
140                final Section s = this.stack.pop();
141                s.setEndingLine( line );
142 
143                if ( this.stack.isEmpty() )
144                {
145                    this.stack = null;
146                    throw new IOException( this.getMessage( "unexpectedEndOfSection", new Object[]
147                        {
148                            s.getName() == null ? "/" : s.getName()
149                        } ) );
150 
151                }
152 
153                if ( this.stack.peek().getName() == null && this.stack.size() > 1 )
154                {
155                    this.stack.pop();
156                }
157            }
158            else
159            {
160                switch ( current.getMode() )
161                {
162                    case Section.MODE_HEAD:
163                        current.getHeadContent().append( line ).append( this.getLineSeparator() );
164                        break;
165 
166                    case Section.MODE_TAIL:
167                        current.getTailContent().append( line ).append( this.getLineSeparator() );
168                        break;
169 
170                    default:
171                        throw new AssertionError( current.getMode() );
172 
173                }
174            }
175        }
176        else
177        {
178            final Section root = this.stack.pop();
179 
180            if ( !this.stack.isEmpty() )
181            {
182                this.stack = null;
183                throw new IOException( this.getMessage( "unexpectedEndOfSection", new Object[]
184                    {
185                        root.getName() == null ? "/" : root.getName()
186                    } ) );
187 
188            }
189 
190            replacement = this.getOutput( root );
191            this.stack = null;
192        }
193 
194        return replacement;
195    }
196 
197    /**
198     * Parses the given line to mark the start of a new section.
199     *
200     * @param line The line to parse.
201     *
202     * @return The section starting at {@code line} or {@code null} if {@code line} does not mark the start of a
203     * section.
204     */
205    protected Section getSection( final String line )
206    {
207        Section s = null;
208 
209        if ( line != null )
210        {
211            final int startIndex = line.indexOf( DEFAULT_SECTION_START );
212            if ( startIndex != -1 )
213            {
214                final String name = line.substring( startIndex + DEFAULT_SECTION_START.length(),
215                                                    line.indexOf( ']', startIndex + DEFAULT_SECTION_START.length() ) );
216 
217                s = new Section();
218                s.setName( name );
219            }
220        }
221 
222        return s;
223    }
224 
225    /**
226     * Parses the given line to mark the end of a section.
227     *
228     * @param line The line to parse.
229     *
230     * @return {@code true} if {@code line} marks the end of a section; {@code false} if {@code line} does not mark the
231     * end of a section.
232     */
233    protected boolean isSectionFinished( final String line )
234    {
235        return line != null && line.indexOf( DEFAULT_SECTION_END ) != -1;
236    }
237 
238    /**
239     * Edits a section.
240     * <p>This method does not change any content by default. Overriding classes may use this method for editing
241     * sections prior to rendering.</p>
242     *
243     * @param section The section to edit.
244     *
245     * @throws NullPointerException if {@code section} is {@code null}.
246     * @throws IOException if editing fails.
247     */
248    protected void editSection( final Section section ) throws IOException
249    {
250        if ( section == null )
251        {
252            throw new NullPointerException( "section" );
253        }
254    }
255 
256    /**
257     * Edits a section recursively.
258     *
259     * @param section The section to edit recursively.
260     *
261     * @throws NullPointerException if {@code section} is {@code null}.
262     * @throws IOException if editing fails.
263     */
264    private void editSections( final Section section ) throws IOException
265    {
266        if ( section == null )
267        {
268            throw new NullPointerException( "section" );
269        }
270 
271        this.editSection( section );
272        for ( Section child : section.getSections() )
273        {
274            this.editSections( child );
275        }
276    }
277 
278    /**
279     * Gets the output of the editor.
280     * <p>This method calls method {@code editSection()} for each section of the editor prior to rendering the sections
281     * to produce the output of the editor.</p>
282     *
283     * @param section The section to start rendering the editor's output with.
284     *
285     * @return The output of the editor.
286     *
287     * @throws NullPointerException if {@code section} is {@code null}.
288     * @throws IOException if editing or rendering fails.
289     */
290    protected String getOutput( final Section section ) throws IOException
291    {
292        if ( section == null )
293        {
294            throw new NullPointerException( "section" );
295        }
296 
297        this.editSections( section );
298        return this.renderSections( section, new StringBuilder() ).toString();
299    }
300 
301    /**
302     * Appends the content of a given section to a given buffer.
303     *
304     * @param section The section to render.
305     * @param buffer The buffer to append the content of {@code section} to.
306     *
307     * @return {@code buffer} with content of {@code section} appended.
308     */
309    private StringBuilder renderSections( final Section section, final StringBuilder buffer )
310    {
311        if ( section.getStartingLine() != null )
312        {
313            buffer.append( section.getStartingLine() ).append( this.getLineSeparator() );
314        }
315 
316        buffer.append( section.getHeadContent() );
317 
318        for ( Section child : section.getSections() )
319        {
320            this.renderSections( child, buffer );
321        }
322 
323        buffer.append( section.getTailContent() );
324 
325        if ( section.getEndingLine() != null )
326        {
327            buffer.append( section.getEndingLine() ).append( this.getLineSeparator() );
328        }
329 
330        return buffer;
331    }
332 
333    private String getMessage( final String key, final Object arguments )
334    {
335        return new MessageFormat( ResourceBundle.getBundle( SectionEditor.class.getName().
336            replace( '.', '/' ) ).getString( key ) ).format( arguments );
337 
338    }
339 
340}

[all classes][org.jomc.util]
EMMA 2.0.5312 (C) Vladimir Roubtsov