001package Torello.HTML; 002 003import Torello.Java.StringParse; 004import Torello.Java.Additional.Ret2; 005import Torello.JavaDoc.StaticFunctional; 006 007import java.util.Vector; 008import java.util.Iterator; 009 010/** 011 * The means by which the {@link Replaceable} interface may be used to efficiently update a 012 * modified HTML-{@code Vector}, <B STYLE='color: red;'><I>quickly, and all-at-once</I></B>. 013 * 014 * <BR /><BR />This class has but two public methods. The both accept a single 015 * Java-{@code Vector}, and either a single {@link Replaceable}, or a list of 016 * {@link Replaceable Replaceable's}. These {@code Replaceable's} must have been 'derived' from 017 * that self-same input {@code Vector}. 018 * 019 * <BR /><BR />The class will iterate those User-Provided {@code Replaceable's}, 020 * modifying the contents of the original / input {@code Vector} to reflect those changes. 021 * 022 * <BR /><BR /><B CLASS=JDDescLabel2>Efficient-Transformations</B> 023 * 024 * <BR />The sole benefit of the {@link Replaceable}-{@code interface} is such that it allows a 025 * programmer to avoid large numbers of <B STYLE='color: red;'>array-shift</B> and 026 * <B STYLE='color: red;'>array-copy</B> operations. 027 * 028 * <BR /><BR />If a user decides to modify the contents of an HTML-Table, for instance, he should 029 * first copy the {@link HTMLNode}-Contents of that table out of an input-{@code Vector} and into a 030 * Java-HTML {@link SubSection} instance. Afterwards, the {@link HTMLNode}-Content of that 031 * {@code SubSection} may be modified in any way that the user sees fit. 032 * 033 * <BR /><BR />{@code SubSection's} of HTML have vastly smaller amounts of nodes inside 034 * their internal-array's (the Java-Package {@code 'java.util.*'} data-structures have array's 035 * among their private data-fields). Because the amount of nodes in an HTML-{@code SubSection} 036 * will always be fewer, the number of node-shifts that are required to modify that HTML will, 037 * obviously, always be tremendously fewer too. 038 * 039 * <BR /><BR />By using the {@link Replaceable}-{@code interface} classes, a user may hold off the 040 * innefficent transformation of a Vectorized-HTML until all changes have been decided, at which 041 * point, they may be progpogated back into the original HTML-{@code Vector}, in a single pass 042 * (using this class' sole-method) -<B STYLE='color: red;'><I>thereby avoiding large number of 043 * array-copy and shift operations!</I></B> 044 * 045 * @see SubSection 046 * @see NodeIndex 047 */ 048@Torello.JavaDoc.StaticFunctional 049public class Replacement 050{ 051 private Replacement() {} 052 053 /** 054 * <EMBED CLASS='external-html' DATA-FILE-ID=REPLACEMENT_MDESC> 055 * 056 * @param page <EMBED CLASS='external-html' DATA-FILE-ID=REPLACEMENT_MPAGE> 057 * @param updatedReplaceables <EMBED CLASS='external-html' DATA-FILE-ID=REPLACEMENT_UREPLS> 058 * 059 * @param updateReplaceablesAfterBuild 060 * <EMBED CLASS='external-html' DATA-FILE-ID=REPLACEMENT_URAB> 061 * 062 * @return <EMBED CLASS='external-html' DATA-FILE-ID=REPLACEMENT_MRET> 063 * <!-- NOTE: This HTML (for the Return-Description) is borrowed and used in ReplaceNodes --> 064 * 065 * @throws ReplaceableOutOfBoundsException If any of the {@link Replaceable} instances 066 * returned by the {@code 'updatedReplaceables'} iterator-parameter have <B><I>original 067 * location</I></B> {@code Vector}-indices that are not within the bounds of the HTML 068 * page-{@code Vector} (parameter {@code 'page'}). 069 * 070 * @throws ReplaceablesOverlappingException If any of the {@link Replaceable} instances 071 * returned by the {@code 'updatedReplaceables'} iterator-parameter have <B><I>original 072 * location</I></B> {@code Vector}-indices that overlap. 073 * 074 * @throws ReplaceablesUnsortedException If any of the {@link Replaceable} instances returned 075 * by the {@code 'updatedReplaceables'} iterator-parameter have <B><I>original-starting 076 * locations</I></B> that are non-consecutive (out of order!) 077 * 078 * <!-- In the JD Comments 'external-html/' directory, this is filed under 'r0/' --> 079 */ 080 public static 081 Ret2<Vector<HTMLNode>, Vector<Replaceable>> 082 run( 083 final Vector<HTMLNode> page, 084 final Iterable<? extends Replaceable> updatedReplaceables, 085 final boolean updateReplaceablesAfterBuild 086 ) 087 { 088 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 089 // First check for the case that 'updatedReplaceables' is empty 090 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 091 092 final int SIZE = page.size(); 093 094 // This entire loop is merely done for nothing more than error/exception checking. 095 // It is mandatory that the SubSections which are passed are all 'in-order', that 096 // none of them overlap, and that they all fit inside the 'page' vector parameter. 097 098 Iterator<? extends Replaceable> iter = updatedReplaceables.iterator(); 099 100 // If there are no Replaceables in the Iterable, return the original page. 101 if (! iter.hasNext()) 102 { 103 if (! updateReplaceablesAfterBuild) return new Ret2<>(page, null); 104 105 Vector<Replaceable> ret = new Vector<>(); 106 for (Replaceable r : updatedReplaceables) ret.add(r); 107 return new Ret2<>(page, ret); 108 } 109 110 111 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 112 // Initialize the Loop variables 113 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 114 115 Replaceable replaceable = iter.next(); 116 Replaceable previousReplaceable = null; 117 118 // These are used, specifically, for the error-checking part of the loop 119 int start1 = replaceable.originalLocationStart(); 120 int end1 = replaceable.originalLocationEnd() - 1; // Value is Exclusive 121 int start2 = -1; 122 int end2 = -1; 123 124 // This is used for the exception messages only. It is incremented on the last line of the 125 // loop body. 126 127 int i=0; 128 129 // These are used, specifically, for the part that computes the size the final vector 130 int size = 0; // Total (Future) Size of the Return / Output Vector 131 int last = 0; // Temp Variable, it is easier to have a separate one for this 132 133 while (iter.hasNext()) 134 { 135 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 136 // Compute what the size of the returned HTML-Vector is going to be. 137 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 138 // 139 // This is done by looking at the locations of all the replacements, and the number of 140 // nodes between each replacement. 141 // 142 // For the part of this loop that is computing the size of the final vector, there 143 // start2 and end2 pointers should just be ignored. The start1, end1 pointer pair 144 // are sufficient, as during each iteration, start2 and end2 are assigned to start1 and 145 // end1 in the very next step anyways. 146 147 size += (start1 - last); // Size of the previous "in-between chunk" 148 size += replaceable.currentSize(); // Size of the next SubSection 149 last = end1; // advance the 'last' pointer 150 151 152 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 153 // Advance the Validity-Checking Pointer Pairs 154 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 155 156 // Advance pointer-pair #1 (but DONT'T do this one the VERY FIRST ITERATION) 157 if (i > 0) 158 { 159 start1 = start2; 160 end1 = end2; 161 } 162 163 // Advance Pointer Pair #2 164 previousReplaceable = replaceable; 165 replaceable = iter.next(); 166 start2 = replaceable.originalLocationStart(); 167 end2 = replaceable.originalLocationEnd() - 1; 168 169 170 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 171 // NOW... THE VALIDITY-CHECKING IF-STATEMENTS 172 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 173 174 /* 175 System.out.println( 176 "previousReplaceable: " + previousReplaceable + 177 ", replaceable: " + replaceable + '\n' + 178 "start1: " + start1 + ", end1: " + end1 + ", start2: " + start2 + ", end2: " + end2 179 ); 180 */ 181 182 if (start2 < start1) 183 184 throw new ReplaceablesUnsortedException( 185 "'updatedReplaceables' contains at least one Replaceable Element-Pair " + 186 "which is not sorted from first to last:\n" + 187 "The " + (i+1) + StringParse.ordinalIndicator(i+1) + " Replaceable returned " + 188 "by 'updatedReplaceables' starts at page-index " + start1 + '\n' + 189 "The " + (i+2) + StringParse.ordinalIndicator(i+2) + " Replaceable returned " + 190 "by 'updatedReplaceables' starts at page-index " + start2, 191 previousReplaceable, replaceable 192 ); 193 194 195 if ( (start2 == start1) // New section starts at same place as the previous section 196 || (start2 <= end1) // New section begins before the previous section ended 197 198 // !!! Whenever a user has created a zero-length-replaceable (zero original length) 199 // then the "end" of that replaceable will be "start-1". Sounds a little silly, 200 // right? Well inserting a zero-length replaceable happens a lot in JavaDoc 201 // Upgrader. The two cases of the if-statement are both important. Remember, the 202 // iterator must be returning sorted elements, or else the previous if statement 203 // would have already failed. 204 ) 205 206 throw new ReplaceablesOverlappingException( 207 "'updatedReplaceables' contains at least one Replaceable Element-Pair " + 208 "that overlap each-other:\n" + 209 "The " + (i+1) + StringParse.ordinalIndicator(i+1) + " Replaceable returned " + 210 "by 'updatedReplaceables' has original-location " + 211 "[" + start1 + ", " + end1 + "]\n" + 212 "The " + (i+2) + StringParse.ordinalIndicator(i+2) + " Replaceable returned " + 213 "by 'updatedReplaceables' has original-location " + 214 "[" + start2 + ", " + end2 + ']', 215 previousReplaceable, replaceable 216 ); 217 218 if (end1 > SIZE) 219 220 throw new ReplaceableOutOfBoundsException( 221 "There was a Replaceable Element whose original-location was not within the " + 222 "bounds of page:\n" + 223 "The " + (i+1) + StringParse.ordinalIndicator(i+1) + " Replaceable returned " + 224 "by 'updatedReplaceables' has original-location " + 225 "[" + start1 + ", " + end1 + "]\n" + 226 "While page.size() is: " + SIZE, 227 replaceable 228 ); 229 230 i++; 231 } 232 233 234 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 235 // POST-LOOP FINISHING TOUCHES 236 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 237 238 // "Ending Read" Check. The very last location is not checked, because the loop breaks 239 // before it gets to check pointer-pair-2 (on the last iteration) 240 // 241 // NOTE: The patholigical-cae where there is **ONLY ONE** SubSection in the updatedReplaceables 242 // input Collection. If pointer-pair-2 is -1, there is no need to check it... :) 243 // If (pointer-pair-2 == -1), the loop body was never entered 244 245 if (end2 != -1) if (end2 >= SIZE) 246 247 throw new ReplaceableOutOfBoundsException( 248 "There was a Replaceable Element whose original-location was not within the " + 249 "bounds of page:\n" + 250 "The " + (i+2) + StringParse.ordinalIndicator(i+2) + " Replaceable returned by " + 251 "'updatedReplaceables' has original-location [" + start2 + ", " + end2 + "]\n" + 252 "While page.size() is: " + SIZE, 253 replaceable 254 ); 255 256 // the very-last replaceable was not added to the size. 257 size += (start1 - last); // Size of the previous "in-between chunk" 258 size += replaceable.currentSize(); // Size of the next SubSection 259 last = end1; 260 261 262 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 263 // Build the Return Vector - NOTE - We just computed its final size! 264 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 265 // 266 // ALSO: If the user has requested the DP's be udated, also build the "newSubSections" Vec 267 // 268 // The purpose of the above computation was for instantiating a properly-sized vector 269 // at construction time. This will save quite a bit of time that would be wasted on 270 // vector resizing. 271 272 Vector<HTMLNode> ret = new Vector<>(size); 273 274 275 // By user request, only! This really isn't *THAT* important. All that the 276 // 'newSubSections' Vector will have shall be the exact same-subsections that are passed 277 // as a parameter to this method through the 'updatedReplaceables' parameter - *EXCEPT* that 278 // their SubSection.location fields will be updated to hold the *ACTUAL* / *NEW* locations 279 280 Vector<Replaceable> newReplaceables = 281 updateReplaceablesAfterBuild ? new Vector<>() : null; 282 283 284 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 285 // MAIN-LOOP: Iterate each of the Replaceables that was passed as input to this method. 286 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 287 // 288 // Add their contents to Output-Vector, and make sure to add all "in-between" nodes too! 289 290 // The index-pointer to the **ORIGINAL-VECTOR** (a.k.a. the input vector) 291 int pagePos=0; 292 293 294 // This loop does the replacement. It is quick and easy if you understand what replacing 295 // a list of subsections involves. 296 297 for (Replaceable r : updatedReplaceables) 298 { 299 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 300 // Add all of the MOST-RECENT "In-Between Nodes" (These are all nodes before next SS) 301 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 302 // 303 // AFTERWARDS: Add all nodes in the next Sub-Section 304 305 // Retrieve all of the 'in-between' nodes 306 while (pagePos < r.originalLocationStart()) ret.add(page.elementAt(pagePos++)); 307 308 // Add this Replaceable to the returned output list! 309 r.addAllInto(ret); 310 311 // Skip over the old nodes. 312 pagePos = r.originalLocationEnd(); // don't add one, value is exclusive 313 314 315 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 316 // User may request that the Sub-Section 'Locations' be updated, rather than discarded 317 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 318 319 // This is done for convenience so that the user knows where the sections are all 320 // located in the new build. 321 // 322 // NOTE: All this is doing is changing the 'location' field of the old subsection 323 // which has changed to contain the new 'location' 324 325 if (updateReplaceablesAfterBuild) 326 { 327 int ePos = ret.size(); 328 int sPos = ePos - r.currentSize() + 1; 329 330 newReplaceables.add(r.moveAndUpdate(sPos)); 331 } 332 } 333 334 335 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 336 // IMPORTANT: Add the last / final Elements that occur *AFTER* the *LAST* Sub-Section 337 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 338 // 339 // This part should also be called "The Tail" of the Page. (Put the Page-Tail back) 340 341 while (pagePos < page.size()) ret.add(page.elementAt(pagePos++)); 342 343 344 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 345 // AGAIN: User may request that Sub-Section 'Locations' be updated, rather than discarded 346 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 347 348 return updateReplaceablesAfterBuild 349 ? new Ret2<>(ret, newReplaceables) 350 : new Ret2<>(ret, null); 351 } 352}