litesoft
@ 964
litesoft / trunk / GWT_Sandbox / FormEngine / src / com / temp / common / shared / utils / HtmlFriendlySectionCodec.java
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
Revision | Author | Commited | Message |
---|---|---|---|
964 | GeorgeS | Fri 01 Aug, 2014 03:18:23 +0000 | ! |