Subversion Repository Public Repository

litesoft

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package com.temp.common.shared.utils;

import java.util.*;

/**
 * Class to encode (& package) AND decode (& unpackage) multiple strings to and
 * from a single HTML Friendly String.
 * <p/>
 * Because IE will process this data and apply the "whitespace" rule - all
 * consecutive whitespace character(s) are converted to a SINGLE space, the
 * encoded form must NOT have whitespace in it unless it is already a single
 * space!
 *
 * @author georgs
 */
public class HtmlFriendlySectionCodec {
    private static final char SPACE = ' ';
    private static final char NEWLINE = '\n';
    private static final char[] SPACE_OPTIONS = {'~', '^'};
    private static final char[] NEWLINE_OPTIONS = {'|', '#'};
    private static final char[] SEPARATOR_OPTIONS = {'=', '*'};

    /**
     * Encode the sections to a single HTML friendly string.
     *
     * @param sections !null or empty, and no array entries may be null
     *
     * @return single HTML friendly string ready to be decoded
     */
    public static String encode( String... sections ) {
        String space = determineSeparator( SPACE_OPTIONS, 1, sections );
        String newLine = determineSeparator( NEWLINE_OPTIONS, 1, sections );
        String separator = determineSeparator( SEPARATOR_OPTIONS, 8, sections );

        StringBuilder sb = new StringBuilder();
        // Header
        sb.append( newLine ).append( SPACE ).append( space ).append( SPACE ).append( separator ).append( SPACE ).append( newLine ).append( NEWLINE );
        // Sections
        SectionCodec sectionCodec = new SectionCodec( space, newLine );
        for ( String section : sections ) {
            sectionCodec.encode( section, sb );
            sb.append( NEWLINE ).append( separator ).append( NEWLINE );
        }
        return sb.toString();
    }

    /**
     * Decode the sections from a single HTML friendly string.
     *
     * @param encodedSections !null or empty (must have at least the Header - see above)
     *
     * @return the decoded sections
     */
    public static String[] decode( String encodedSections ) {
        // Header
        int spAt1 = encodedSections.indexOf( SPACE );
        int spAt2 = encodedSections.indexOf( SPACE, spAt1 + 1 );
        int spAt3 = encodedSections.indexOf( SPACE, spAt2 + 1 );
        if ( spAt3 == -1 ) {
            throw invalidHeader( encodedSections );
        }
        String newLine = Assert.noEmpty( "Header newLine", encodedSections.substring( 0, spAt1 ) );
        String space = Assert.noEmpty( "Header space", encodedSections.substring( spAt1 + 1, spAt2 ) );
        String separator = Assert.noEmpty( "Header separator", encodedSections.substring( spAt2 + 1, spAt3 ) );

        if ( !encodedSections.substring( spAt3 + 1 ).startsWith( newLine ) ) {
            throw invalidHeader( encodedSections );
        }
        // Sections
        String sectionsText = encodedSections.substring( spAt3 + 1 + newLine.length() ).replace( NEWLINE, SPACE ).trim();
        if ( sectionsText.length() == 0 ) {
            return new String[0]; // Empty Array
        }
        SectionCodec sectionCodec = new SectionCodec( space, newLine );
        List<String> sections = new ArrayList<String>();
        for ( int at; -1 != (at = sectionsText.indexOf( separator )); sectionsText = sectionsText.substring( at + separator.length() ).trim() ) {
            String section = sectionsText.substring( 0, at );
            sections.add( sectionCodec.decode( section ) );
        }
        if ( sectionsText.length() != 0 ) {
            throw new IllegalArgumentException( "Junk after last section: '" + sectionsText + "'" );
        }
        return sections.toArray( new String[sections.size()] );
    }

    private static IllegalArgumentException invalidHeader( String encodedSections ) {
        return new IllegalArgumentException( "Unable to extract the 'Header' from " + encodedSections );
    }

    private static String determineSeparator( char[] options, int lengthFactor, String[] sections ) {
        for ( int blockSize = lengthFactor; true; blockSize += lengthFactor ) {
            for ( char option : options ) {
                StringBuilder sb = new StringBuilder( blockSize );
                for ( int i = 0; i < blockSize; i++ ) {
                    sb.append( option );
                }
                boolean acceptable = true;
                for ( String section : sections ) {
                    if ( section.contains( sb ) ) {
                        acceptable = false;
                        break;
                    }
                }
                if ( acceptable ) {
                    return sb.toString(); // The Separator we'll use
                }
            }
        }
    }

    static class SectionCodec {
        private String space;
        private String newLine;

        public SectionCodec( String space, String newLine ) {
            this.space = space;
            this.newLine = newLine;
        }

        public void encode( String section, StringBuilder sb ) {
            for ( int i = 0; i < section.length(); i++ ) {
                char c = section.charAt( i );
                if ( c == SPACE ) {
                    sb.append( space );
                } else if ( c == NEWLINE ) {
                    sb.append( newLine ).append( NEWLINE );
                } else {
                    HtmlEntity.append( c, sb );
                }
            }
        }

        public String decode( String section ) {
            StringBuilder sb = new StringBuilder( section );
            purgeAll( sb, SPACE );
            purgeAll( sb, NEWLINE );
            replaceAll( sb, space, SPACE );
            replaceAll( sb, newLine, NEWLINE );
            int ampAt = sb.indexOf( "&" );
            if ( ampAt != -1 ) {
                int from = 0;
                do {
                    String entity = extractEntity( sb, ampAt );
                    char c = HtmlEntity.fromStringForm( entity );
                    replace( sb, entity, ampAt, c );
                    from = ampAt + 1;
                } while ( -1 != (ampAt = sb.indexOf( "&", from )) );
            }
            return sb.toString();
        }

        private String extractEntity( StringBuilder sb, int ampAt ) {
            int semiAt = sb.indexOf( ";", ampAt );
            if ( semiAt == -1 ) {
                throw new IllegalArgumentException( "Not an Html Entity starting at: " + sb.substring( ampAt ) );
            }
            return sb.substring( ampAt, semiAt + 1 );
        }

        private void purgeAll( StringBuilder sb, char characterToRemove ) {
            for ( int i = sb.length() - 1; 0 <= i; i-- ) {
                if ( characterToRemove == sb.charAt( i ) ) {
                    sb.deleteCharAt( i );
                }
            }
        }

        private void replaceAll( StringBuilder sb, String stringToReplace, char replacementChar ) {
            for ( int at; -1 != (at = sb.indexOf( stringToReplace )); ) {
                replace( sb, stringToReplace, at, replacementChar );
            }
        }

        private void replace( StringBuilder sb, String stringToReplace, int at, char replacementChar ) {
            // remove all but one
            sb.delete( at, at + (stringToReplace.length() - 1) );
            // then replace the one remaining!
            sb.setCharAt( at, replacementChar );
        }
    }
}

Commits for litesoft/trunk/GWT_Sandbox/FormEngine/src/com/temp/common/shared/utils/HtmlFriendlySectionCodec.java

Diff revisions: vs.
Revision Author Commited Message
964 GeorgeS picture GeorgeS Fri 01 Aug, 2014 03:18:23 +0000

!