EMMA Coverage Report (generated Wed Mar 26 15:19:37 CET 2014)
[all classes][org.jomc.util]

COVERAGE SUMMARY FOR SOURCE FILE [SectionEditor.java]

nameclass, %method, %block, %line, %
SectionEditor.java100% (1/1)92%  (12/13)94%  (422/448)94%  (87/93)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SectionEditor100% (1/1)92%  (12/13)94%  (422/448)94%  (87/93)
SectionEditor (String): void 0%   (0/1)0%   (0/5)0%   (0/2)
editSection (Section): void 100% (1/1)72%  (13/18)80%  (4/5)
getOutput (Section): String 100% (1/1)77%  (17/22)80%  (4/5)
editSections (Section): void 100% (1/1)83%  (24/29)83%  (5/6)
editLine (String): String 100% (1/1)97%  (200/206)98%  (44/45)
SectionEditor (): void 100% (1/1)100% (5/5)100% (2/2)
SectionEditor (LineEditor): void 100% (1/1)100% (5/5)100% (2/2)
SectionEditor (LineEditor, String): void 100% (1/1)100% (10/10)100% (3/3)
getMessage (String, Object []): String 100% (1/1)100% (11/11)100% (1/1)
getSection (String): Section 100% (1/1)100% (54/54)100% (11/11)
isSectionFinished (String): boolean 100% (1/1)100% (11/11)100% (1/1)
isSectionPresent (String): boolean 100% (1/1)100% (18/18)100% (1/1)
renderSections (Section, StringBuilder): StringBuilder 100% (1/1)100% (54/54)100% (9/9)

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: SectionEditor.java 4613 2012-09-22 10:07:08Z schulte $
29 *
30 */
31package org.jomc.util;
32 
33import java.io.IOException;
34import java.text.MessageFormat;
35import java.util.HashMap;
36import java.util.Map;
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@schulte.it">Christian Schulte</a>
48 * @version $JOMC: SectionEditor.java 4613 2012-09-22 10:07:08Z schulte $
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    /** Mapping of section names to flags indicating presence of the section. */
65    private final Map<String, Boolean> presenceFlags = new HashMap<String, Boolean>();
66 
67    /** Creates a new {@code SectionEditor} instance. */
68    public SectionEditor()
69    {
70        this( null, null );
71    }
72 
73    /**
74     * Creates a new {@code SectionEditor} instance taking a string to use for separating lines.
75     *
76     * @param lineSeparator String to use for separating lines.
77     */
78    public SectionEditor( final String lineSeparator )
79    {
80        this( null, lineSeparator );
81    }
82 
83    /**
84     * Creates a new {@code SectionEditor} instance taking an editor to chain.
85     *
86     * @param editor The editor to chain.
87     */
88    public SectionEditor( final LineEditor editor )
89    {
90        this( editor, null );
91    }
92 
93    /**
94     * Creates a new {@code SectionEditor} 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 SectionEditor( final LineEditor editor, final String lineSeparator )
100    {
101        super( editor, lineSeparator );
102    }
103 
104    @Override
105    protected final String editLine( final String line ) throws IOException
106    {
107        if ( this.stack == null )
108        {
109            final Section root = new Section();
110            root.setMode( Section.MODE_HEAD );
111            this.stack = new Stack<Section>();
112            this.stack.push( root );
113        }
114 
115        Section current = this.stack.peek();
116        String replacement = null;
117 
118        if ( line != null )
119        {
120            final Section child = this.getSection( line );
121 
122            if ( child != null )
123            {
124                child.setStartingLine( line );
125                child.setMode( Section.MODE_HEAD );
126 
127                if ( current.getMode() == Section.MODE_TAIL && current.getTailContent().length() > 0 )
128                {
129                    final Section s = new Section();
130                    s.getHeadContent().append( current.getTailContent() );
131                    current.getTailContent().setLength( 0 );
132                    current.getSections().add( s );
133                    current = s;
134                    this.stack.push( current );
135                }
136 
137                current.getSections().add( child );
138                current.setMode( Section.MODE_TAIL );
139                this.stack.push( child );
140            }
141            else if ( this.isSectionFinished( line ) )
142            {
143                final Section s = this.stack.pop();
144                s.setEndingLine( line );
145 
146                if ( this.stack.isEmpty() )
147                {
148                    this.stack = null;
149                    throw new IOException( getMessage( "unexpectedEndOfSection", this.getLineNumber() ) );
150                }
151 
152                if ( this.stack.peek().getName() == null && this.stack.size() > 1 )
153                {
154                    this.stack.pop();
155                }
156            }
157            else
158            {
159                switch ( current.getMode() )
160                {
161                    case Section.MODE_HEAD:
162                        current.getHeadContent().append( line ).append( this.getLineSeparator() );
163                        break;
164 
165                    case Section.MODE_TAIL:
166                        current.getTailContent().append( line ).append( this.getLineSeparator() );
167                        break;
168 
169                    default:
170                        throw new AssertionError( current.getMode() );
171 
172                }
173            }
174        }
175        else
176        {
177            final Section root = this.stack.pop();
178 
179            if ( !this.stack.isEmpty() )
180            {
181                this.stack = null;
182                throw new IOException( getMessage( "unexpectedEndOfFile", this.getLineNumber(), root.getName() ) );
183            }
184 
185            replacement = this.getOutput( root );
186            this.stack = null;
187        }
188 
189        return replacement;
190    }
191 
192    /**
193     * Parses the given line to mark the start of a new section.
194     *
195     * @param line The line to parse or {@code null}.
196     *
197     * @return The section starting at {@code line} or {@code null}, if {@code line} does not mark the start of a
198     * section.
199     *
200     * @throws IOException if parsing fails.
201     */
202    protected Section getSection( final String line ) throws IOException
203    {
204        Section s = null;
205 
206        if ( line != null )
207        {
208            final int markerIndex = line.indexOf( DEFAULT_SECTION_START );
209 
210            if ( markerIndex != -1 )
211            {
212                final int startIndex = markerIndex + DEFAULT_SECTION_START.length();
213                final int endIndex = line.indexOf( ']', startIndex );
214 
215                if ( endIndex == -1 )
216                {
217                    throw new IOException( getMessage( "sectionMarkerParseFailure", line, this.getLineNumber() ) );
218                }
219 
220                s = new Section();
221                s.setName( line.substring( startIndex, endIndex ) );
222            }
223        }
224 
225        return s;
226    }
227 
228    /**
229     * Parses the given line to mark the end of a section.
230     *
231     * @param line The line to parse or {@code null}.
232     *
233     * @return {@code true}, if {@code line} marks the end of a section; {@code false}, if {@code line} does not mark
234     * the end of a section.
235     *
236     * @throws IOException if parsing fails.
237     */
238    protected boolean isSectionFinished( final String line ) throws IOException
239    {
240        return line != null && line.indexOf( DEFAULT_SECTION_END ) != -1;
241    }
242 
243    /**
244     * Edits a section.
245     * <p>This method does not change any content by default. Overriding classes may use this method for editing
246     * sections prior to rendering.</p>
247     *
248     * @param section The section to edit.
249     *
250     * @throws NullPointerException if {@code section} is {@code null}.
251     * @throws IOException if editing fails.
252     */
253    protected void editSection( final Section section ) throws IOException
254    {
255        if ( section == null )
256        {
257            throw new NullPointerException( "section" );
258        }
259 
260        if ( section.getName() != null )
261        {
262            this.presenceFlags.put( section.getName(), Boolean.TRUE );
263        }
264    }
265 
266    /**
267     * Edits a section recursively.
268     *
269     * @param section The section to edit recursively.
270     *
271     * @throws NullPointerException if {@code section} is {@code null}.
272     * @throws IOException if editing fails.
273     */
274    private void editSections( final Section section ) throws IOException
275    {
276        if ( section == null )
277        {
278            throw new NullPointerException( "section" );
279        }
280 
281        this.editSection( section );
282        for ( int i = 0, s0 = section.getSections().size(); i < s0; i++ )
283        {
284            this.editSections( section.getSections().get( i ) );
285        }
286    }
287 
288    /**
289     * Gets the output of the editor.
290     * <p>This method calls method {@code editSection()} for each section of the editor prior to rendering the sections
291     * to produce the output of the editor.</p>
292     *
293     * @param section The section to start rendering the editor's output with.
294     *
295     * @return The output of the editor.
296     *
297     * @throws NullPointerException if {@code section} is {@code null}.
298     * @throws IOException if editing or rendering fails.
299     */
300    protected String getOutput( final Section section ) throws IOException
301    {
302        if ( section == null )
303        {
304            throw new NullPointerException( "section" );
305        }
306 
307        this.presenceFlags.clear();
308        this.editSections( section );
309        return this.renderSections( section, new StringBuilder( 512 ) ).toString();
310    }
311 
312    /**
313     * Gets a flag indicating that the input of the editor contained a named section.
314     *
315     * @param sectionName The name of the section to test or {@code null}.
316     *
317     * @return {@code true}, if the input of the editor contained a section with name {@code sectionName};
318     * {@code false}, if the input of the editor did not contain a section with name {@code sectionName}.
319     */
320    public boolean isSectionPresent( final String sectionName )
321    {
322        return sectionName != null && this.presenceFlags.get( sectionName ) != null
323               && this.presenceFlags.get( sectionName ).booleanValue();
324 
325    }
326 
327    /**
328     * Appends the content of a given section to a given buffer.
329     *
330     * @param section The section to render.
331     * @param buffer The buffer to append the content of {@code section} to.
332     *
333     * @return {@code buffer} with content of {@code section} appended.
334     */
335    private StringBuilder renderSections( final Section section, final StringBuilder buffer )
336    {
337        if ( section.getStartingLine() != null )
338        {
339            buffer.append( section.getStartingLine() ).append( this.getLineSeparator() );
340        }
341 
342        buffer.append( section.getHeadContent() );
343 
344        for ( int i = 0, s0 = section.getSections().size(); i < s0; i++ )
345        {
346            this.renderSections( section.getSections().get( i ), buffer );
347        }
348 
349        buffer.append( section.getTailContent() );
350 
351        if ( section.getEndingLine() != null )
352        {
353            buffer.append( section.getEndingLine() ).append( this.getLineSeparator() );
354        }
355 
356        return buffer;
357    }
358 
359    private static String getMessage( final String key, final Object... arguments )
360    {
361        return MessageFormat.format( ResourceBundle.getBundle( SectionEditor.class.getName().
362            replace( '.', '/' ) ).getString( key ), arguments );
363 
364    }
365 
366}

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