001 /*
002 Copyright (c) 1996-2012, Damon Hart-Davis
003 All rights reserved.
004
005 Redistribution and use in source and binary forms, with or without
006 modification, are permitted provided that the following conditions are
007 met:
008
009 * Redistributions of source code must retain the above copyright
010 notice, this list of conditions and the following disclaimer.
011
012 * Redistributions in binary form must reproduce the above copyright
013 notice, this list of conditions and the following disclaimer in the
014 documentation and/or other materials provided with the
015 distribution.
016
017 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
018 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
019 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
020 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
021 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
022 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
024 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
025 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
027 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028 */
029 /*
030 * Created by IntelliJ IDEA.
031 * User: Administrator
032 * Date: 28-Dec-02
033 * Time: 22:24:51
034 * To change template for new class use
035 * Code Style | Class Templates options (Tools | IDE Options).
036 */
037 package org.hd.d.pg2k.test.dev;
038
039 import java.awt.Dimension;
040 import java.io.ByteArrayInputStream;
041 import java.io.ByteArrayOutputStream;
042 import java.io.DataInputStream;
043 import java.io.DataOutputStream;
044 import java.io.File;
045 import java.io.IOException;
046 import java.io.InputStream;
047 import java.io.ObjectInputStream;
048 import java.io.ObjectOutputStream;
049 import java.io.OutputStream;
050 import java.io.Serializable;
051 import java.util.ArrayList;
052 import java.util.Arrays;
053 import java.util.HashSet;
054 import java.util.Iterator;
055 import java.util.List;
056 import java.util.Map;
057 import java.util.Properties;
058 import java.util.Random;
059 import java.util.Set;
060 import java.util.TreeMap;
061 import java.util.concurrent.ConcurrentHashMap;
062 import java.util.concurrent.ConcurrentMap;
063 import java.util.concurrent.atomic.AtomicInteger;
064 import java.util.zip.GZIPInputStream;
065 import java.util.zip.GZIPOutputStream;
066
067 import junit.framework.TestCase;
068 import net.contrapunctus.lzma.LzmaInputStream;
069 import net.contrapunctus.lzma.LzmaOutputStream;
070
071 import org.apache.tools.bzip2.CBZip2InputStream;
072 import org.apache.tools.bzip2.CBZip2OutputStream;
073 import org.hd.d.pg2k.svrCore.AccessionData;
074 import org.hd.d.pg2k.svrCore.AllExhibitImmutableData;
075 import org.hd.d.pg2k.svrCore.AllExhibitProperties;
076 import org.hd.d.pg2k.svrCore.AllExhibitPropertiesDelta;
077 import org.hd.d.pg2k.svrCore.CS8Bit;
078 import org.hd.d.pg2k.svrCore.Compact7BitString;
079 import org.hd.d.pg2k.svrCore.CompressionLevel;
080 import org.hd.d.pg2k.svrCore.CoreConsts;
081 import org.hd.d.pg2k.svrCore.EPGIDiff;
082 import org.hd.d.pg2k.svrCore.ExhibitPropsComputable;
083 import org.hd.d.pg2k.svrCore.ExhibitPropsGlobalImmutable;
084 import org.hd.d.pg2k.svrCore.ExhibitPropsLoadable;
085 import org.hd.d.pg2k.svrCore.ExhibitStaticAttr;
086 import org.hd.d.pg2k.svrCore.ExhibitThumbnails;
087 import org.hd.d.pg2k.svrCore.FileTools;
088 import org.hd.d.pg2k.svrCore.GenUtils;
089 import org.hd.d.pg2k.svrCore.Name;
090 import org.hd.d.pg2k.svrCore.ROByteArray;
091 import org.hd.d.pg2k.svrCore.TextUtils;
092 import org.hd.d.pg2k.svrCore.Tuple;
093 import org.hd.d.pg2k.svrCore.Tuple.Pair;
094 import org.hd.d.pg2k.svrCore.datasource.ExhibitDataFileSource;
095 import org.hd.d.pg2k.svrCore.datasource.simpleCache.MetaData;
096 import org.hd.d.pg2k.svrCore.location.Location;
097 import org.hd.d.pg2k.svrCore.location.LocationMap;
098 import org.hd.d.pg2k.svrCore.props.GenProps;
099 import org.hd.d.pg2k.svrCore.props.LocalProps;
100 import org.hd.d.pg2k.svrCore.props.PropertiesBundleDiff;
101 import org.hd.d.pg2k.svrCore.props.PropertiesDiff;
102 import org.hd.d.pg2k.svrCore.vars.EventPeriod;
103 import org.hd.d.pg2k.svrCore.vars.EventVariableValue;
104 import org.hd.d.pg2k.svrCore.vars.SimpleVariableDefinition;
105 import org.hd.d.pg2k.svrCore.vars.SimpleVariableValue;
106 import org.hd.d.pg2k.svrCore.vars.SystemVariables;
107 import org.hd.d.pg2k.webSvr.util.FlushableGZIPOutputStream;
108 import org.hd.d.pg2k.webSvr.util.WebUtils;
109 import org.mutabilitydetector.unittesting.AllowedReason;
110 import org.mutabilitydetector.unittesting.MutabilityAssert;
111 import org.mutabilitydetector.unittesting.MutabilityMatchers;
112
113 /**Test that critical classes can be correctly serialised and deserialised.
114 * This is useful for items that will be:
115 * <ul>
116 * <li>Cached/persisted to disc, especially those that must last a long time and are valuable.
117 * <li>Sent over the wire.
118 * </ul>
119 */
120 public final class SerializationTest extends TestCase
121 {
122 public SerializationTest(final String name)
123 {
124 super(name);
125 }
126
127 /**If true, print out serialised value in form that can be cut-n-paste back into test code. */
128 private static final boolean PRINT_SER_BYTES = false;
129
130 /**Maximum bytes to print on each line of serialised form. */
131 private static final int MAX_BYTES_PER_LINE = 8;
132
133 /**Check that the object passed can be serialised and deserialised.
134 *
135 * @param obj must implement java.io.Serializable; never null
136 * @return the deserialised version of the object
137 * @throws IOException
138 * @throws ClassNotFoundException
139 */
140 public static Object checkObjectCanBeSerialisedAndDeserialised(final Object obj)
141 throws IOException,
142 ClassNotFoundException
143 {
144 assertTrue("Original object does not implement Serializable", obj instanceof Serializable);
145
146 // Serialise and deserialise to make a deep copy as if sent over the wire...
147 final byte[] data = serialiseToByteArray(obj);
148
149 if(PRINT_SER_BYTES)
150 { dumpSerData(obj, data); }
151
152 final Object obj2 = deserialiseFromByteArray(data);
153
154 assertNotNull("Deserialisation of a class " + obj.getClass() + " resulted in a null object", obj2);
155 return(obj2);
156 }
157
158 /**Serialise given object to byte array; never null. */
159 public static byte[] serialiseToByteArray(final Object obj)
160 throws IOException
161 {
162 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
163 final ObjectOutputStream oos = new ObjectOutputStream(baos);
164 oos.writeObject(obj);
165 oos.flush();
166 final byte data[] = baos.toByteArray();
167 return data;
168 }
169
170 /**Dump serialised data in a form suitable to copy-and-paste into code. */
171 private static void dumpSerData(final Object obj, final byte[] data)
172 {
173 System.out.println("***DUMPING DATA FOR " + obj);
174 System.out.println("private static final byte serData[/*"+data.length+"*/] = {");
175 int count = 0;
176 final byte values[] = new byte[MAX_BYTES_PER_LINE];
177 final StringBuilder sbTmp = new StringBuilder(); // Used to build one value to print.
178 for(final byte b : data)
179 {
180 sbTmp.setLength(0);
181 sbTmp.append(Integer.toString(b, 10));
182 while(sbTmp.length() < 4) { sbTmp.insert(0, ' '); }
183 sbTmp.append(", ");
184 System.out.print(sbTmp.toString());
185 values[count] = b;
186 if(++count >= MAX_BYTES_PER_LINE)
187 {
188 dumpAsciiComment(sbTmp, count, values);
189 count = 0;
190 }
191 }
192 dumpAsciiComment(sbTmp, count, values);
193 System.out.println("};");
194 }
195
196 /**Dump to System.out "count" characters from values, substituting non-printable-ASCII. */
197 private static void dumpAsciiComment(final StringBuilder sbTmp, final int count, final byte[] values)
198 {
199 sbTmp.setLength(0);
200 sbTmp.append("// ");
201 for(int i = 0; i < count; ++i)
202 {
203 final int bc = values[i];
204 if((bc > 32) && (bc < 127)) { sbTmp.append((char) bc); }
205 else { sbTmp.append('.'); /* Substitute "safe" character. */ }
206 }
207 System.out.println(sbTmp.toString());
208 }
209
210 /**Deserialise an Object from the supplied serialised form. */
211 static Object deserialiseFromByteArray(final byte[] data)
212 throws IOException, ClassNotFoundException
213 {
214 final ByteArrayInputStream bais = new ByteArrayInputStream(data);
215 final ObjectInputStream ois = new ObjectInputStream(bais);
216 final Object obj2 = ois.readObject();
217 return obj2;
218 }
219
220 /**Check that the passed (non-null) object compares equals after being serialised/deserialised.
221 * This looks at equals() and hashCode().
222 * <p>
223 * Tests in passing that the object <em>can</em> be (de)serialised.
224 *
225 * @throws Exception in case of difficulty.
226 * @return deserialised object in case we want to perform extra tests on it
227 */
228 public static Object checkSerialisationPreservesEquality(final Object obj)
229 throws Exception
230 {
231 final Object obj2 = checkObjectCanBeSerialisedAndDeserialised(obj);
232
233 assertTrue("Equality test fails for orig.equals(copy)", obj.equals(obj2));
234 assertTrue("Equality test fails for copy.equals(orig)", obj2.equals(obj));
235 assertTrue("Hash codes must be equal", obj.hashCode() == obj2.hashCode());
236
237 return(obj2);
238 }
239
240 /**Basic tests for those objects whose empty instances should have zero hash and compare equal.
241 * (Empty instances are those made with the default constructor.)
242 * <p>
243 * Most of our persistable and transmittable-across-the-wire objects
244 * should show this property so that we can easily tell empty objects
245 * from ones containing useful data for example.
246 * <p>
247 * These objects should also be Serializable ((de)serializable) and still
248 * compare equal.
249 *
250 * @throws Exception in case of difficulty.
251 */
252 public static void checkEmptyInstancesAreZeroAndEqualAndSerializable(final Class<? extends Serializable> c)
253 throws Exception
254 {
255 final Object empty = c.newInstance();
256 assertTrue(empty.hashCode() == 0);
257 assertTrue(empty.equals(c.newInstance()));
258 checkSerialisationPreservesEquality(empty);
259 }
260
261 /**Test AllExhibitImmutableData for (de)serialisability.
262 * We check that:
263 * <ul>
264 * <li>Empty instances are constructed correctly, with a zero timestamp
265 * (and a zero hash, and compare equal).
266 * <li>Non-empty items can be serialised and deserialised correctly,
267 * including compute-on-demand cached values.
268 * </ul>
269 */
270 public static void testAllExhibitImmutableData()
271 throws Exception
272 {
273 checkEmptyInstancesAreZeroAndEqualAndSerializable(AllExhibitImmutableData.class);
274 final AllExhibitImmutableData empty = new AllExhibitImmutableData();
275 assertTrue(empty.timestamp == 0);
276 assertTrue(empty.getAllExhibitNamesSorted().isEmpty());
277 assertTrue(empty.getAllStaticAttrs().isEmpty());
278
279 // TODO: finish!
280 }
281
282 /**Test AllExhibitProperties for (de)serialisability.
283 * We check that:
284 * <ul>
285 * <li>Empty instances are constructed correctly, with a zero timestamp
286 * (and a zero hash, and compare equal).
287 * <li>Non-empty items can be serialised and deserialised correctly,
288 * including compute-on-demand cached values.
289 * </ul>
290 */
291 public static void testAllExhibitProperties()
292 throws Exception
293 {
294 checkEmptyInstancesAreZeroAndEqualAndSerializable(AllExhibitProperties.class);
295 final AllExhibitProperties empty = new AllExhibitProperties();
296 assertTrue(empty.longHash == 0);
297
298 // Load a real instance from the filesystem...
299 final ExhibitDataFileSource edfs = new ExhibitDataFileSource(null);
300 final AllExhibitProperties aep = edfs.getAllExhibitProperties(-1);
301 Main.getOut().println("Loaded AEP with exhibit count: " + aep.aeid.length);
302 // Check that the AEP can be (de)serialised correctly.
303 final Object aep2 = checkObjectCanBeSerialisedAndDeserialised(aep);
304 // In case of legitimate hash changes from serialised state
305 // we'll check equality based on the reserialising the deserialised instance.
306 checkSerialisationPreservesEquality(aep2);
307 // TODO: finish!
308 }
309
310 /**Basic tests on ExhibitPropsLoadable. */
311 public static void testExhibitPropsLoadable()
312 throws Exception
313 {
314 checkSerialisationPreservesEquality(ExhibitPropsLoadable.EMPTY);
315 // TODO: finish!
316 }
317
318 /**Test GenProps for (de)serialisability.
319 * We check that:
320 * <ul>
321 * <li>Empty instances are constructed correctly, with a zero timestamp
322 * (and a zero hash, and compare equal).
323 * <li>Non-empty items can be serialised and deserialised correctly,
324 * including compute-on-demand cached values.
325 * </ul>
326 */
327 public static void testGenProps()
328 throws Exception
329 {
330 final GenProps empty = new GenProps();
331 assertTrue(empty.timestamp == 0);
332 final GenProps empty2 =
333 (GenProps) checkObjectCanBeSerialisedAndDeserialised(empty);
334 assertTrue(empty2.timestamp == 0);
335
336 // TODO: finish!
337 }
338
339 /**Test ExhibitThumbnails.
340 * We want to check that object resolving works and we discard
341 * duplicate values that equals NO_THUMBNAILS on deserialisation.
342 */
343 public static void testExhibitThumbnails()
344 throws Exception
345 {
346 final Object obj2 = checkSerialisationPreservesEquality(ExhibitThumbnails.NO_THUMBNAILS);
347 assertTrue("ExhibitThumbnails.NO_THUMBNAILS should resolve to itself (ie discard duplicates)",
348 obj2 == ExhibitThumbnails.NO_THUMBNAILS);
349
350 // TODO: some tests on more realistic thumbnail objects.
351 }
352
353 /**Test Location data (esp Estd) can be (de)serialised.
354 * We also test the LocationMap.
355 */
356 public static void testLocation()
357 throws Exception
358 {
359 // Set up some properties as if from a file:
360 // type=Estd
361 // # The error in each value is +/- the Err value.
362 // # Degrees East/North.
363 // E=-0.29
364 // EErr=0.01
365 // N=51.41
366 // NErr=0.01
367 final Properties p1 = new Properties();
368 p1.setProperty("type", "Estd");
369 p1.setProperty("E", "-0.29");
370 p1.setProperty("EErr", "0.01");
371 p1.setProperty("N", "51.41");
372 p1.setProperty("NErr", "0.01");
373 // Construct a simple example Estd item.
374 final Location.Estd e1 = new Location.Estd(
375 true, // Claim that this data is "specific" to a particular exhibit.
376 "", // No prefix on the property names.
377 p1 // The properties.
378 );
379 // Check that we can (de)serialise this (and preserve equality).
380 checkSerialisationPreservesEquality(e1);
381
382 // Test behaviour of entire LocationMap.
383 checkEmptyInstancesAreZeroAndEqualAndSerializable(LocationMap.class);
384 }
385
386 /**Raw value of String variable value for "hills are alive" value. */
387 private static final String hillsAreAlive = " the hills are alive! 87239473075!^%#^";
388
389 /**Serialised String variable value for "hills are alive" value. */
390 private static final byte serData_hillsAreAlive_20050130[/*482*/] = {
391 -84, -19, 0, 5, 115, 114, 0, 46, // ....sr..
392 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
393 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
394 114, 67, 111, 114, 101, 46, 118, 97, // rCore.va
395 114, 115, 46, 83, 105, 109, 112, 108, // rs.Simpl
396 101, 86, 97, 114, 105, 97, 98, 108, // eVariabl
397 101, 86, 97, 108, 117, 101, -71, 77, // eValue.M
398 83, -103, -28, -60, -107, 95, 2, 0, // S...._..
399 4, 74, 0, 9, 116, 105, 109, 101, // .J..time
400 115, 116, 97, 109, 112, 76, 0, 3, // stampL..
401 100, 101, 102, 116, 0, 53, 76, 111, // deft.5Lo
402 114, 103, 47, 104, 100, 47, 100, 47, // rg/hd/d/
403 112, 103, 50, 107, 47, 115, 118, 114, // pg2k/svr
404 67, 111, 114, 101, 47, 118, 97, 114, // Core/var
405 115, 47, 83, 105, 109, 112, 108, 101, // s/Simple
406 86, 97, 114, 105, 97, 98, 108, 101, // Variable
407 68, 101, 102, 105, 110, 105, 116, 105, // Definiti
408 111, 110, 59, 76, 0, 9, 103, 108, // on;L..gl
409 111, 98, 97, 108, 77, 97, 112, 116, // obalMapt
410 0, 15, 76, 106, 97, 118, 97, 47, // ..Ljava/
411 117, 116, 105, 108, 47, 77, 97, 112, // util/Map
412 59, 76, 0, 5, 118, 97, 108, 117, // ;L..valu
413 101, 116, 0, 18, 76, 106, 97, 118, // et..Ljav
414 97, 47, 108, 97, 110, 103, 47, 79, // a/lang/O
415 98, 106, 101, 99, 116, 59, 120, 112, // bject;xp
416 0, 0, 1, 1, -60, -115, 49, 29, // ......1.
417 115, 114, 0, 51, 111, 114, 103, 46, // sr.3org.
418 104, 100, 46, 100, 46, 112, 103, 50, // hd.d.pg2
419 107, 46, 115, 118, 114, 67, 111, 114, // k.svrCor
420 101, 46, 118, 97, 114, 115, 46, 83, // e.vars.S
421 105, 109, 112, 108, 101, 86, 97, 114, // impleVar
422 105, 97, 98, 108, 101, 68, 101, 102, // iableDef
423 105, 110, 105, 116, 105, 111, 110, 32, // inition.
424 -60, -92, 85, 126, 79, -65, -35, 2, // ..U~O...
425 0, 8, 90, 0, 5, 101, 118, 101, // ..Z..eve
426 110, 116, 90, 0, 5, 108, 111, 99, // ntZ..loc
427 97, 108, 73, 0, 17, 109, 97, 120, // alI..max
428 68, 105, 102, 102, 69, 118, 101, 110, // DiffEven
429 116, 67, 111, 117, 110, 116, 90, 0, // tCountZ.
430 10, 112, 101, 114, 115, 105, 115, 116, // .persist
431 101, 110, 116, 90, 0, 8, 114, 101, // entZ..re
432 97, 100, 79, 110, 108, 121, 73, 0, // adOnlyI.
433 4, 116, 121, 112, 101, 76, 0, 14, // .typeL..
434 101, 118, 80, 101, 114, 105, 111, 100, // evPeriod
435 83, 117, 98, 115, 101, 116, 116, 0, // Subsett.
436 19, 76, 106, 97, 118, 97, 47, 117, // .Ljava/u
437 116, 105, 108, 47, 69, 110, 117, 109, // til/Enum
438 83, 101, 116, 59, 76, 0, 4, 110, // Set;L..n
439 97, 109, 101, 116, 0, 18, 76, 106, // amet..Lj
440 97, 118, 97, 47, 108, 97, 110, 103, // ava/lang
441 47, 83, 116, 114, 105, 110, 103, 59, // /String;
442 120, 112, 0, 1, 0, 0, 0, 0, // xp......
443 0, 0, 0, 0, 0, 2, 112, 116, // ......pt
444 0, 14, 116, 101, 115, 116, 83, 101, // ..testSe
445 114, 46, 118, 97, 108, 117, 101, 51, // r.value3
446 112, 116, 0, 38, 32, 116, 104, 101, // pt.&.the
447 32, 104, 105, 108, 108, 115, 32, 97, // .hills.a
448 114, 101, 32, 97, 108, 105, 118, 101, // re.alive
449 33, 32, 56, 55, 50, 51, 57, 52, // !.872394
450 55, 51, 48, 55, 53, 33, 94, 37, // 73075!^%
451 35, 94, // #^
452 };
453
454 /**Test that variables can be correctly serialised/deserialised.
455 * We check all the built-in definitions and
456 * instances/values of several types.
457 */
458 public static void testSimpleVariables()
459 throws Exception
460 {
461 // Test that every definition can be correctly serialised and deserialised.
462 for(final Iterator it = SystemVariables.defs.iterator(); it.hasNext(); )
463 { checkSerialisationPreservesEquality(it.next()); }
464
465 // Test an instance of TYPE_NONE...
466 final SimpleVariableValue svv1 =
467 new SimpleVariableValue(SystemVariables.KEEP_ALIVE,
468 null);
469 checkSerialisationPreservesEquality(svv1);
470
471 // Test a simple instance of TYPE_NUMBER...
472 final SimpleVariableValue svv2 =
473 new SimpleVariableValue(SystemVariables.ThroughputMonitorFilter_CLIENT_COUNT,
474 new Integer(rnd.nextInt(9999)));
475 checkSerialisationPreservesEquality(svv2);
476
477 // Try this on a few random variable definitions
478 // plus all extant system variable values.
479 final List<SimpleVariableDefinition> testDefSet = new ArrayList<SimpleVariableDefinition>();
480 testDefSet.addAll(SystemVariables.defs);
481 for(int i = 37; --i >= 0; )
482 { testDefSet.add(SystemVariablesTest.makeRandomSimpleVariableDefinition()); }
483
484 // Try out one possible value for each test definition.
485 for(final Iterator it = testDefSet.iterator(); it.hasNext(); )
486 {
487 final SimpleVariableDefinition def =
488 (SimpleVariableDefinition) it.next();
489 final SimpleVariableValue svv = SystemVariablesTest.makeRandomSimpleVariableValue(def);
490 checkSerialisationPreservesEquality(svv);
491 }
492
493 // Check that we can deserialise at least one frozen serialised (String) non-standard value.
494 // This is a test of robustness of both the definition and the value objects.
495 final SimpleVariableDefinition newDef = new SimpleVariableDefinition("testSer.value3",
496 SimpleVariableDefinition.TYPE_STRING);
497 final SimpleVariableValue svv3 =
498 new SimpleVariableValue(newDef, hillsAreAlive);
499 checkSerialisationPreservesEquality(svv3);
500 // Now check that deserialising the static data gives an equal value.
501 assertTrue("Deserialised frozen value must be equal to newly constructed one",
502 svv3.equals(deserialiseFromByteArray(serData_hillsAreAlive_20050130)));
503 }
504
505
506 /**First test string value for EVV; has a count of 1. */
507 private static final String evvTestString1 = "45dgjytref";
508
509 /**Second test string value for EVV; has a count of 2. */
510 private static final String evvTestString2 = "dfgtr67kjhfh";
511
512 /**Total event count in map. */
513 private static final int evv_TOTAL_EVENT_COUNT = 5;
514
515 /**Old serialised format of a single EVV with embedded Map<Object, ValueInfo>. */
516 private static final byte serData_evv1_20050130_1748[/*812*/] = {
517 -84, -19, 0, 5, 115, 114, 0, 45, // ....sr.-
518 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
519 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
520 114, 67, 111, 114, 101, 46, 118, 97, // rCore.va
521 114, 115, 46, 69, 118, 101, 110, 116, // rs.Event
522 86, 97, 114, 105, 97, 98, 108, 101, // Variable
523 86, 97, 108, 117, 101, 70, -78, -84, // ValueF..
524 102, 92, -52, 21, -95, 2, 0, 6, // f\......
525 90, 0, 13, 97, 117, 116, 104, 111, // Z..autho
526 114, 97, 116, 105, 116, 105, 118, 101, // ratitive
527 74, 0, 14, 105, 110, 116, 101, 114, // J..inter
528 118, 97, 108, 78, 117, 109, 98, 101, // valNumbe
529 114, 73, 0, 15, 116, 111, 116, 97, // rI..tota
530 108, 69, 118, 101, 110, 116, 67, 111, // lEventCo
531 117, 110, 116, 76, 0, 3, 100, 101, // untL..de
532 102, 116, 0, 53, 76, 111, 114, 103, // ft.5Lorg
533 47, 104, 100, 47, 100, 47, 112, 103, // /hd/d/pg
534 50, 107, 47, 115, 118, 114, 67, 111, // 2k/svrCo
535 114, 101, 47, 118, 97, 114, 115, 47, // re/vars/
536 83, 105, 109, 112, 108, 101, 86, 97, // SimpleVa
537 114, 105, 97, 98, 108, 101, 68, 101, // riableDe
538 102, 105, 110, 105, 116, 105, 111, 110, // finition
539 59, 76, 0, 4, 105, 110, 102, 111, // ;L..info
540 116, 0, 15, 76, 106, 97, 118, 97, // t..Ljava
541 47, 117, 116, 105, 108, 47, 77, 97, // /util/Ma
542 112, 59, 76, 0, 6, 112, 101, 114, // p;L..per
543 105, 111, 100, 116, 0, 40, 76, 111, // iodt.(Lo
544 114, 103, 47, 104, 100, 47, 100, 47, // rg/hd/d/
545 112, 103, 50, 107, 47, 115, 118, 114, // pg2k/svr
546 67, 111, 114, 101, 47, 118, 97, 114, // Core/var
547 115, 47, 69, 118, 101, 110, 116, 80, // s/EventP
548 101, 114, 105, 111, 100, 59, 120, 112, // eriod;xp
549 1, 0, 0, 0, 0, 0, 0, 0, // ........
550 1, 0, 0, 0, 5, 115, 114, 0, // .....sr.
551 51, 111, 114, 103, 46, 104, 100, 46, // 3org.hd.
552 100, 46, 112, 103, 50, 107, 46, 115, // d.pg2k.s
553 118, 114, 67, 111, 114, 101, 46, 118, // vrCore.v
554 97, 114, 115, 46, 83, 105, 109, 112, // ars.Simp
555 108, 101, 86, 97, 114, 105, 97, 98, // leVariab
556 108, 101, 68, 101, 102, 105, 110, 105, // leDefini
557 116, 105, 111, 110, 32, -60, -92, 85, // tion...U
558 126, 79, -65, -35, 2, 0, 8, 90, // ~O.....Z
559 0, 5, 101, 118, 101, 110, 116, 90, // ..eventZ
560 0, 5, 108, 111, 99, 97, 108, 73, // ..localI
561 0, 17, 109, 97, 120, 68, 105, 102, // ..maxDif
562 102, 69, 118, 101, 110, 116, 67, 111, // fEventCo
563 117, 110, 116, 90, 0, 10, 112, 101, // untZ..pe
564 114, 115, 105, 115, 116, 101, 110, 116, // rsistent
565 90, 0, 8, 114, 101, 97, 100, 79, // Z..readO
566 110, 108, 121, 73, 0, 4, 116, 121, // nlyI..ty
567 112, 101, 76, 0, 14, 101, 118, 80, // peL..evP
568 101, 114, 105, 111, 100, 83, 117, 98, // eriodSub
569 115, 101, 116, 116, 0, 19, 76, 106, // sett..Lj
570 97, 118, 97, 47, 117, 116, 105, 108, // ava/util
571 47, 69, 110, 117, 109, 83, 101, 116, // /EnumSet
572 59, 76, 0, 4, 110, 97, 109, 101, // ;L..name
573 116, 0, 18, 76, 106, 97, 118, 97, // t..Ljava
574 47, 108, 97, 110, 103, 47, 83, 116, // /lang/St
575 114, 105, 110, 103, 59, 120, 112, 1, // ring;xp.
576 0, 0, 0, 0, 101, 1, 0, 0, // ....e...
577 0, 0, 2, 112, 116, 0, 18, 116, // ...pt..t
578 101, 115, 116, 83, 101, 114, 46, 115, // estSer.s
579 116, 114, 105, 110, 103, 46, 118, 97, // tring.va
580 108, 115, 114, 0, 17, 106, 97, 118, // lsr..jav
581 97, 46, 117, 116, 105, 108, 46, 72, // a.util.H
582 97, 115, 104, 77, 97, 112, 5, 7, // ashMap..
583 -38, -63, -61, 22, 96, -47, 3, 0, // ....`...
584 2, 70, 0, 10, 108, 111, 97, 100, // .F..load
585 70, 97, 99, 116, 111, 114, 73, 0, // FactorI.
586 9, 116, 104, 114, 101, 115, 104, 111, // .thresho
587 108, 100, 120, 112, 63, 64, 0, 0, // ldxp?@..
588 0, 0, 0, 3, 119, 8, 0, 0, // ....w...
589 0, 4, 0, 0, 0, 2, 116, 0, // ......t.
590 10, 52, 53, 100, 103, 106, 121, 116, // .45dgjyt
591 114, 101, 102, 115, 114, 0, 55, 111, // refsr.7o
592 114, 103, 46, 104, 100, 46, 100, 46, // rg.hd.d.
593 112, 103, 50, 107, 46, 115, 118, 114, // pg2k.svr
594 67, 111, 114, 101, 46, 118, 97, 114, // Core.var
595 115, 46, 69, 118, 101, 110, 116, 86, // s.EventV
596 97, 114, 105, 97, 98, 108, 101, 86, // ariableV
597 97, 108, 117, 101, 36, 86, 97, 108, // alue$Val
598 117, 101, 73, 110, 102, 111, 12, 82, // ueInfo.R
599 -114, -88, -5, -45, 19, 93, 2, 0, // .....]..
600 2, 73, 0, 5, 99, 111, 117, 110, // .I..coun
601 116, 73, 0, 4, 114, 97, 110, 107, // tI..rank
602 120, 112, 0, 0, 0, 1, 0, 0, // xp......
603 0, 1, 116, 0, 12, 100, 102, 103, // ..t..dfg
604 116, 114, 54, 55, 107, 106, 104, 102, // tr67kjhf
605 104, 115, 113, 0, 126, 0, 13, 0, // hsq.~...
606 0, 0, 2, 0, 0, 0, 0, 120, // .......x
607 126, 114, 0, 38, 111, 114, 103, 46, // ~r.&org.
608 104, 100, 46, 100, 46, 112, 103, 50, // hd.d.pg2
609 107, 46, 115, 118, 114, 67, 111, 114, // k.svrCor
610 101, 46, 118, 97, 114, 115, 46, 69, // e.vars.E
611 118, 101, 110, 116, 80, 101, 114, 105, // ventPeri
612 111, 100, 0, 0, 0, 0, 0, 0, // od......
613 0, 0, 18, 0, 0, 120, 114, 0, // .....xr.
614 14, 106, 97, 118, 97, 46, 108, 97, // .java.la
615 110, 103, 46, 69, 110, 117, 109, 0, // ng.Enum.
616 0, 0, 0, 0, 0, 0, 0, 18, // ........
617 0, 0, 120, 112, 116, 0, 5, 86, // ..xpt..V
618 76, 79, 78, 71, // LONG
619 };
620
621 /**New (more compact) serialised format of a single EVV with counts[]/values[]. */
622 private static final byte serData_evv1_20050131_1512[/*743*/] = {
623 -84, -19, 0, 5, 115, 114, 0, 45, // ....sr.-
624 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
625 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
626 114, 67, 111, 114, 101, 46, 118, 97, // rCore.va
627 114, 115, 46, 69, 118, 101, 110, 116, // rs.Event
628 86, 97, 114, 105, 97, 98, 108, 101, // Variable
629 86, 97, 108, 117, 101, 70, -78, -84, // ValueF..
630 102, 92, -52, 21, -95, 3, 0, 8, // f\......
631 90, 0, 13, 97, 117, 116, 104, 111, // Z..autho
632 114, 97, 116, 105, 116, 105, 118, 101, // ratitive
633 74, 0, 14, 105, 110, 116, 101, 114, // J..inter
634 118, 97, 108, 78, 117, 109, 98, 101, // valNumbe
635 114, 73, 0, 15, 116, 111, 116, 97, // rI..tota
636 108, 69, 118, 101, 110, 116, 67, 111, // lEventCo
637 117, 110, 116, 91, 0, 6, 99, 111, // unt[..co
638 117, 110, 116, 115, 116, 0, 2, 91, // untst..[
639 73, 76, 0, 3, 100, 101, 102, 116, // IL..deft
640 0, 53, 76, 111, 114, 103, 47, 104, // .5Lorg/h
641 100, 47, 100, 47, 112, 103, 50, 107, // d/d/pg2k
642 47, 115, 118, 114, 67, 111, 114, 101, // /svrCore
643 47, 118, 97, 114, 115, 47, 83, 105, // /vars/Si
644 109, 112, 108, 101, 86, 97, 114, 105, // mpleVari
645 97, 98, 108, 101, 68, 101, 102, 105, // ableDefi
646 110, 105, 116, 105, 111, 110, 59, 76, // nition;L
647 0, 4, 105, 110, 102, 111, 116, 0, // ..infot.
648 15, 76, 106, 97, 118, 97, 47, 117, // .Ljava/u
649 116, 105, 108, 47, 77, 97, 112, 59, // til/Map;
650 76, 0, 6, 112, 101, 114, 105, 111, // L..perio
651 100, 116, 0, 40, 76, 111, 114, 103, // dt.(Lorg
652 47, 104, 100, 47, 100, 47, 112, 103, // /hd/d/pg
653 50, 107, 47, 115, 118, 114, 67, 111, // 2k/svrCo
654 114, 101, 47, 118, 97, 114, 115, 47, // re/vars/
655 69, 118, 101, 110, 116, 80, 101, 114, // EventPer
656 105, 111, 100, 59, 91, 0, 6, 118, // iod;[..v
657 97, 108, 117, 101, 115, 116, 0, 19, // aluest..
658 91, 76, 106, 97, 118, 97, 47, 108, // [Ljava/l
659 97, 110, 103, 47, 79, 98, 106, 101, // ang/Obje
660 99, 116, 59, 120, 112, 1, 0, 0, // ct;xp...
661 0, 0, 0, 0, 0, 1, 0, 0, // ........
662 0, 5, 117, 114, 0, 2, 91, 73, // ..ur..[I
663 77, -70, 96, 38, 118, -22, -78, -91, // M.`&v...
664 2, 0, 0, 120, 112, 0, 0, 0, // ...xp...
665 2, 0, 0, 0, 2, 0, 0, 0, // ........
666 1, 115, 114, 0, 51, 111, 114, 103, // .sr.3org
667 46, 104, 100, 46, 100, 46, 112, 103, // .hd.d.pg
668 50, 107, 46, 115, 118, 114, 67, 111, // 2k.svrCo
669 114, 101, 46, 118, 97, 114, 115, 46, // re.vars.
670 83, 105, 109, 112, 108, 101, 86, 97, // SimpleVa
671 114, 105, 97, 98, 108, 101, 68, 101, // riableDe
672 102, 105, 110, 105, 116, 105, 111, 110, // finition
673 32, -60, -92, 85, 126, 79, -65, -35, // ...U~O..
674 2, 0, 8, 90, 0, 5, 101, 118, // ...Z..ev
675 101, 110, 116, 90, 0, 5, 108, 111, // entZ..lo
676 99, 97, 108, 73, 0, 17, 109, 97, // calI..ma
677 120, 68, 105, 102, 102, 69, 118, 101, // xDiffEve
678 110, 116, 67, 111, 117, 110, 116, 90, // ntCountZ
679 0, 10, 112, 101, 114, 115, 105, 115, // ..persis
680 116, 101, 110, 116, 90, 0, 8, 114, // tentZ..r
681 101, 97, 100, 79, 110, 108, 121, 73, // eadOnlyI
682 0, 4, 116, 121, 112, 101, 76, 0, // ..typeL.
683 14, 101, 118, 80, 101, 114, 105, 111, // .evPerio
684 100, 83, 117, 98, 115, 101, 116, 116, // dSubsett
685 0, 19, 76, 106, 97, 118, 97, 47, // ..Ljava/
686 117, 116, 105, 108, 47, 69, 110, 117, // util/Enu
687 109, 83, 101, 116, 59, 76, 0, 4, // mSet;L..
688 110, 97, 109, 101, 116, 0, 18, 76, // namet..L
689 106, 97, 118, 97, 47, 108, 97, 110, // java/lan
690 103, 47, 83, 116, 114, 105, 110, 103, // g/String
691 59, 120, 112, 1, 0, 0, 0, 0, // ;xp.....
692 101, 1, 0, 0, 0, 0, 2, 112, // e......p
693 116, 0, 18, 116, 101, 115, 116, 83, // t..testS
694 101, 114, 46, 115, 116, 114, 105, 110, // er.strin
695 103, 46, 118, 97, 108, 112, 126, 114, // g.valp~r
696 0, 38, 111, 114, 103, 46, 104, 100, // .&org.hd
697 46, 100, 46, 112, 103, 50, 107, 46, // .d.pg2k.
698 115, 118, 114, 67, 111, 114, 101, 46, // svrCore.
699 118, 97, 114, 115, 46, 69, 118, 101, // vars.Eve
700 110, 116, 80, 101, 114, 105, 111, 100, // ntPeriod
701 0, 0, 0, 0, 0, 0, 0, 0, // ........
702 18, 0, 0, 120, 114, 0, 14, 106, // ...xr..j
703 97, 118, 97, 46, 108, 97, 110, 103, // ava.lang
704 46, 69, 110, 117, 109, 0, 0, 0, // .Enum...
705 0, 0, 0, 0, 0, 18, 0, 0, // ........
706 120, 112, 116, 0, 5, 86, 76, 79, // xpt..VLO
707 78, 71, 117, 114, 0, 19, 91, 76, // NGur..[L
708 106, 97, 118, 97, 46, 108, 97, 110, // java.lan
709 103, 46, 79, 98, 106, 101, 99, 116, // g.Object
710 59, -112, -50, 88, -97, 16, 115, 41, // ;..X..s)
711 108, 2, 0, 0, 120, 112, 0, 0, // l...xp..
712 0, 2, 116, 0, 12, 100, 102, 103, // ..t..dfg
713 116, 114, 54, 55, 107, 106, 104, 102, // tr67kjhf
714 104, 116, 0, 10, 52, 53, 100, 103, // ht..45dg
715 106, 121, 116, 114, 101, 102, 120, // jytrefx
716 };
717
718 /**Newer serialised format (with Name in place of String on the wire) of a single EVV with counts[]/values[]. */
719 private static final byte serData_evv1_20090806[/*829*/] = {
720 -84, -19, 0, 5, 115, 114, 0, 45, // ....sr.-
721 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
722 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
723 114, 67, 111, 114, 101, 46, 118, 97, // rCore.va
724 114, 115, 46, 69, 118, 101, 110, 116, // rs.Event
725 86, 97, 114, 105, 97, 98, 108, 101, // Variable
726 86, 97, 108, 117, 101, 70, -78, -84, // ValueF..
727 102, 92, -52, 21, -95, 3, 0, 7, // f\......
728 90, 0, 13, 97, 117, 116, 104, 111, // Z..autho
729 114, 97, 116, 105, 116, 105, 118, 101, // ratitive
730 74, 0, 14, 105, 110, 116, 101, 114, // J..inter
731 118, 97, 108, 78, 117, 109, 98, 101, // valNumbe
732 114, 73, 0, 15, 116, 111, 116, 97, // rI..tota
733 108, 69, 118, 101, 110, 116, 67, 111, // lEventCo
734 117, 110, 116, 91, 0, 6, 99, 111, // unt[..co
735 117, 110, 116, 115, 116, 0, 2, 91, // untst..[
736 73, 76, 0, 3, 100, 101, 102, 116, // IL..deft
737 0, 53, 76, 111, 114, 103, 47, 104, // .5Lorg/h
738 100, 47, 100, 47, 112, 103, 50, 107, // d/d/pg2k
739 47, 115, 118, 114, 67, 111, 114, 101, // /svrCore
740 47, 118, 97, 114, 115, 47, 83, 105, // /vars/Si
741 109, 112, 108, 101, 86, 97, 114, 105, // mpleVari
742 97, 98, 108, 101, 68, 101, 102, 105, // ableDefi
743 110, 105, 116, 105, 111, 110, 59, 76, // nition;L
744 0, 6, 112, 101, 114, 105, 111, 100, // ..period
745 116, 0, 40, 76, 111, 114, 103, 47, // t.(Lorg/
746 104, 100, 47, 100, 47, 112, 103, 50, // hd/d/pg2
747 107, 47, 115, 118, 114, 67, 111, 114, // k/svrCor
748 101, 47, 118, 97, 114, 115, 47, 69, // e/vars/E
749 118, 101, 110, 116, 80, 101, 114, 105, // ventPeri
750 111, 100, 59, 91, 0, 6, 118, 97, // od;[..va
751 108, 117, 101, 115, 116, 0, 19, 91, // luest..[
752 76, 106, 97, 118, 97, 47, 108, 97, // Ljava/la
753 110, 103, 47, 79, 98, 106, 101, 99, // ng/Objec
754 116, 59, 120, 112, 1, 0, 0, 0, // t;xp....
755 0, 0, 0, 0, 1, 0, 0, 0, // ........
756 5, 117, 114, 0, 2, 91, 73, 77, // .ur..[IM
757 -70, 96, 38, 118, -22, -78, -91, 2, // .`&v....
758 0, 0, 120, 112, 0, 0, 0, 2, // ..xp....
759 0, 0, 0, 2, 0, 0, 0, 1, // ........
760 115, 114, 0, 51, 111, 114, 103, 46, // sr.3org.
761 104, 100, 46, 100, 46, 112, 103, 50, // hd.d.pg2
762 107, 46, 115, 118, 114, 67, 111, 114, // k.svrCor
763 101, 46, 118, 97, 114, 115, 46, 83, // e.vars.S
764 105, 109, 112, 108, 101, 86, 97, 114, // impleVar
765 105, 97, 98, 108, 101, 68, 101, 102, // iableDef
766 105, 110, 105, 116, 105, 111, 110, 32, // inition.
767 -60, -92, 85, 126, 79, -65, -35, 2, // ..U~O...
768 0, 8, 90, 0, 5, 101, 118, 101, // ..Z..eve
769 110, 116, 90, 0, 5, 108, 111, 99, // ntZ..loc
770 97, 108, 73, 0, 17, 109, 97, 120, // alI..max
771 68, 105, 102, 102, 69, 118, 101, 110, // DiffEven
772 116, 67, 111, 117, 110, 116, 90, 0, // tCountZ.
773 10, 112, 101, 114, 115, 105, 115, 116, // .persist
774 101, 110, 116, 90, 0, 8, 114, 101, // entZ..re
775 97, 100, 79, 110, 108, 121, 73, 0, // adOnlyI.
776 4, 116, 121, 112, 101, 76, 0, 14, // .typeL..
777 101, 118, 80, 101, 114, 105, 111, 100, // evPeriod
778 83, 117, 98, 115, 101, 116, 116, 0, // Subsett.
779 19, 76, 106, 97, 118, 97, 47, 117, // .Ljava/u
780 116, 105, 108, 47, 69, 110, 117, 109, // til/Enum
781 83, 101, 116, 59, 76, 0, 4, 110, // Set;L..n
782 97, 109, 101, 116, 0, 18, 76, 106, // amet..Lj
783 97, 118, 97, 47, 108, 97, 110, 103, // ava/lang
784 47, 83, 116, 114, 105, 110, 103, 59, // /String;
785 120, 112, 1, 0, 0, 0, 0, 101, // xp.....e
786 1, 0, 0, 0, 0, 2, 112, 116, // ......pt
787 0, 18, 116, 101, 115, 116, 83, 101, // ..testSe
788 114, 46, 115, 116, 114, 105, 110, 103, // r.string
789 46, 118, 97, 108, 126, 114, 0, 38, // .val~r.&
790 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
791 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
792 114, 67, 111, 114, 101, 46, 118, 97, // rCore.va
793 114, 115, 46, 69, 118, 101, 110, 116, // rs.Event
794 80, 101, 114, 105, 111, 100, 0, 0, // Period..
795 0, 0, 0, 0, 0, 0, 18, 0, // ........
796 0, 120, 114, 0, 14, 106, 97, 118, // .xr..jav
797 97, 46, 108, 97, 110, 103, 46, 69, // a.lang.E
798 110, 117, 109, 0, 0, 0, 0, 0, // num.....
799 0, 0, 0, 18, 0, 0, 120, 112, // ......xp
800 116, 0, 5, 86, 76, 79, 78, 71, // t..VLONG
801 117, 114, 0, 19, 91, 76, 106, 97, // ur..[Lja
802 118, 97, 46, 108, 97, 110, 103, 46, // va.lang.
803 79, 98, 106, 101, 99, 116, 59, -112, // Object;.
804 -50, 88, -97, 16, 115, 41, 108, 2, // .X..s)l.
805 0, 0, 120, 112, 0, 0, 0, 2, // ..xp....
806 115, 114, 0, 26, 111, 114, 103, 46, // sr..org.
807 104, 100, 46, 100, 46, 112, 103, 50, // hd.d.pg2
808 107, 46, 115, 118, 114, 67, 111, 114, // k.svrCor
809 101, 46, 78, 97, 109, 101, 38, -44, // e.Name&.
810 -30, 17, 1, -80, 121, -16, 3, 0, // ....y...
811 2, 83, 0, 14, 116, 101, 114, 109, // .S..term
812 105, 110, 105, 76, 101, 110, 103, 116, // iniLengt
813 104, 115, 76, 0, 4, 112, 114, 101, // hsL..pre
814 118, 116, 0, 28, 76, 111, 114, 103, // vt..Lorg
815 47, 104, 100, 47, 100, 47, 112, 103, // /hd/d/pg
816 50, 107, 47, 115, 118, 114, 67, 111, // 2k/svrCo
817 114, 101, 47, 78, 97, 109, 101, 59, // re/Name;
818 120, 112, 0, 0, 112, 119, 13, 12, // xp..pw..
819 100, 102, 103, 116, 114, 54, 55, 107, // dfgtr67k
820 106, 104, 102, 104, 120, 115, 113, 0, // jhfhxsq.
821 126, 0, 19, 0, 0, 112, 119, 11, // ~....pw.
822 10, 52, 53, 100, 103, 106, 121, 116, // .45dgjyt
823 114, 101, 102, 120, 120, // refxx
824 };
825
826 /**Test that we can succesfully create and (de)serialise event container objects...
827 * In particular we ensure that we can recover data in older formats where necessary
828 * so that we don't lose hard-to-replace historic data.
829 */
830 public static void testEventVariableValues()
831 throws Exception
832 {
833 // Check that we can deserialise at least one frozen serialised (String) non-standard value.
834 // This is a test of robustness of both the definition and the value objects.
835 final SimpleVariableDefinition newDef = new SimpleVariableDefinition("testSer.string.val",
836 SimpleVariableDefinition.TYPE_STRING,
837 false, // Global.
838 true, // Persistent.
839 false, // Read-write.
840 true, 101, null);
841 final Object values[] = { evvTestString2, evvTestString1 };
842 final int counts[] = { 2, 1 };
843 // final Map<Object, EventVariableValue.ValueInfo> map = new HashMap<Object, EventVariableValue.ValueInfo>();
844 // map.put(evvTestString1, new EventVariableValue.ValueInfo(1, 1));
845 // map.put(evvTestString2, new EventVariableValue.ValueInfo(0, 2));
846 final EventVariableValue evv = new EventVariableValue(true, // Authoritative...
847 newDef,
848 EventPeriod.VLONG,
849 1,
850 evv_TOTAL_EVENT_COUNT,
851 values, counts);
852
853 // Do some simple tests on the constructed value.
854 assertEquals("rank order should be observed", Arrays.asList(new Object[]{evvTestString2,evvTestString1}), evv.getDistinctValuesInRankOrder());
855 assertEquals("sort order should be observed", Arrays.asList(new Object[]{evvTestString1,evvTestString2}), evv.getDistinctValuesSorted());
856
857 // Make a deep copy via deserialisation...
858 final EventVariableValue evvDS = (EventVariableValue)
859 checkSerialisationPreservesEquality(evv);
860 // Reconstruct from byte[] format before the internal format change...
861 final EventVariableValue evvFrozen20050130 = (EventVariableValue)
862 deserialiseFromByteArray(serData_evv1_20050130_1748);
863 // Reconstruct from byte[] more compact format change...
864 final EventVariableValue evvFrozen20050131 = (EventVariableValue)
865 deserialiseFromByteArray(serData_evv1_20050131_1512);
866 // Reconstruct from byte[] even more compact format change (Name in place of String).
867 final EventVariableValue evvFrozen20090806 = (EventVariableValue)
868 deserialiseFromByteArray(serData_evv1_20090806);
869
870 // Perform the same tests on all copies of the value.
871 final EventVariableValue evvs[] = { evv, evvDS, evvFrozen20050130, evvFrozen20050131, evvFrozen20090806 };
872 for(final EventVariableValue e : evvs)
873 {
874 assertTrue("EVV must equal itself", e.equals(e));
875 assertTrue("EVV must equal main copy", e.equals(evv));
876 assertEquals("Total event count must be correct", evv_TOTAL_EVENT_COUNT, e.getTotalEventCount());
877 assertEquals("Total different events must be correct", 2, e.getTotalDistinctValues());
878 assertEquals("Total count of first value must be correct", 1, e.getCount(evvTestString1));
879 assertEquals("Total count of second value must be correct", 2, e.getCount(evvTestString2));
880 assertEquals("Rank of first value must be correct", 1, e.getRank(evvTestString1));
881 assertEquals("Rank of second value must be correct", 0, e.getRank(evvTestString2));
882 assertEquals("Rank of non-existent value must be correct", Integer.MAX_VALUE, e.getRank("not there"));
883 }
884 }
885
886 /**Empty ROByteArray (ie wrapping a byte[0]). */
887 private static final byte serData_ROByteArray_EMPTY_20050522[/*92*/] = {
888 -84, -19, 0, 5, 115, 114, 0, 33, // ....sr.!
889 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
890 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
891 114, 67, 111, 114, 101, 46, 82, 79, // rCore.RO
892 66, 121, 116, 101, 65, 114, 114, 97, // ByteArra
893 121, 52, -45, -14, 29, -75, -97, -121, // y4......
894 -40, 2, 0, 1, 91, 0, 7, 112, // ....[..p
895 97, 121, 108, 111, 97, 100, 116, 0, // ayloadt.
896 2, 91, 66, 120, 112, 117, 114, 0, // .[Bxpur.
897 2, 91, 66, -84, -13, 23, -8, 6, // .[B.....
898 8, 84, -32, 2, 0, 0, 120, 112, // .T....xp
899 0, 0, 0, 0, // ....
900 };
901
902 /**Value to check compression is working and can be correctly undone even on non-ASCII/non-8-bit text. */
903 private static final String ROBA_compS1 = "The quick Brown fox jumped over the PM Blair [satire over, Ed] " +
904 "over and over and over and over and over again until he got bored... " +
905 "(And now here is the news in English: \u7777\u9231\u0000\r\n\t...)";
906
907 /**On-the-wire format on ROBA_compS1. */
908 private static final byte serData_ROBA_compS1_20050522[/*233*/] = {
909 -84, -19, 0, 5, 115, 114, 0, 33, // ....sr.!
910 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
911 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
912 114, 67, 111, 114, 101, 46, 82, 79, // rCore.RO
913 66, 121, 116, 101, 65, 114, 114, 97, // ByteArra
914 121, 52, -45, -14, 29, -75, -97, -121, // y4......
915 -40, 2, 0, 1, 91, 0, 7, 112, // ....[..p
916 97, 121, 108, 111, 97, 100, 116, 0, // ayloadt.
917 2, 91, 66, 120, 112, 117, 114, 0, // .[Bxpur.
918 2, 91, 66, -84, -13, 23, -8, 6, // .[B.....
919 8, 84, -32, 2, 0, 0, 120, 112, // .T....xp
920 0, 0, 0, -115, 99, -40, 25, -110, // ....c...
921 -111, -86, 80, 88, -102, -103, -100, -83, // ..PX....
922 -32, 84, -108, 95, -98, -89, -112, -106, // .T._....
923 95, -95, -112, 85, -102, 91, -112, -102, // _..U.[..
924 -94, -112, 95, -106, 90, -92, 80, 2, // .._.Z.P.
925 -108, 15, -16, 85, 112, -54, 73, -52, // ...Up.I.
926 44, 82, -120, 46, 78, 44, -55, 44, // ,R..N,.,
927 74, 5, -53, -24, 40, -72, -90, -60, // J...(...
928 66, -44, 36, -26, -91, 16, 102, -92, // B.$...f.
929 39, 102, -26, 41, -108, -26, -107, 100, // 'f.)...d
930 -26, 40, 0, -115, 76, -49, 47, 81, // .(..L./Q
931 72, -54, 47, 74, 77, -47, -45, -45, // H./JM...
932 83, -48, 112, 4, -86, -54, -53, 47, // S.p..../
933 7, 74, 0, 13, -49, 44, 6, 91, // .J...,.[
934 -102, -105, 90, 94, -84, 0, -44, -30, // ..Z^....
935 -102, -105, -98, -109, 89, -100, 97, -91, // ....Y.a.
936 -16, 124, -18, -10, -105, 29, 27, 15, // .|......
937 52, -16, 114, 113, 2, 53, 105, 2, // 4.rq.5i.
938 0, // .
939 };
940
941 /**Test handling of ROByteArray.
942 */
943 public static void testROByteArray()
944 throws Exception
945 {
946 //assertImmutable(ROByteArray.class);
947
948 // Check empty instance can be deserialised and is instance controlled!
949 assertEquals("EMPTY ROByteArray must be deserialised correctly.",
950 ROByteArray.EMPTY, checkSerialisationPreservesEquality(ROByteArray.EMPTY));
951 assertSame("EMPTY ROByteArray must be deserialised as itself!",
952 ROByteArray.EMPTY, checkSerialisationPreservesEquality(ROByteArray.EMPTY));
953 // Reconstruct from byte[] format.
954 final ROByteArray emptyDeSer = (ROByteArray)
955 deserialiseFromByteArray(serData_ROByteArray_EMPTY_20050522);
956 assertEquals("Must be able to deserialise original on-the-wire format (EMPTY)",
957 ROByteArray.EMPTY, emptyDeSer);
958 assertEquals("Must get back empty array on deserialisation", 0, emptyDeSer.length());
959
960 // Check non-empty instance can be (de)serialised and (de)compressed.
961 final ROByteArray c1 = ROByteArray.compressFromString(ROBA_compS1);
962 assertEquals("Compressable String in ROByteArray must be deserialised correctly.",
963 c1, checkSerialisationPreservesEquality(c1));
964 final ROByteArray c1DeSer = (ROByteArray)
965 deserialiseFromByteArray(serData_ROBA_compS1_20050522);
966 assertEquals("Must be able to deserialise original on-the-wire format (non-empty, compressed String)",
967 c1, c1DeSer);
968 assertEquals("Must be able to extract String from serialised form",
969 ROBA_compS1, ROByteArray.uncompressToString(c1DeSer));
970 }
971
972 /**Tests of accessions of serialisation of AccessionData.
973 * This is both for Java serialisation and XML format.
974 */
975 public static void testAccessionDataSer()
976 throws Exception
977 {
978 assertSame("deserialising EMPTY should give object == EMPTY",
979 checkSerialisationPreservesEquality(AccessionData.EMPTY),
980 AccessionData.EMPTY);
981
982 // Check that we can (de)serialise via Java native and XML formats.
983 final int NUM_TESTS = 10;
984 for(int i = NUM_TESTS; --i >= 0; )
985 {
986 final byte[] dummyMD5Data = new byte[16];
987 rnd.nextBytes(dummyMD5Data);
988 final boolean b1 = rnd.nextBoolean();
989 final boolean b2 = rnd.nextBoolean();
990 final boolean b3 = rnd.nextBoolean();
991 final boolean b4 = rnd.nextBoolean();
992 final AccessionData ad = new AccessionData(
993 b1 ? null : (System.currentTimeMillis()),
994 b2 ? null : (new Long((rnd.nextLong() >>> 1) | 1)),
995 b3 ? null : (new Integer(rnd.nextInt())),
996 b4 ? null : (new ROByteArray(dummyMD5Data)));
997
998 // Test native-format (de)serialisation...
999 checkSerialisationPreservesEquality(ad);
1000
1001 // Test serialisation via XML format.
1002 final String xml = TextUtils.toXML(ad.getAsDOM(), false, true);
1003 if(i == 0) { System.out.println("[AccessionData XML example: "+xml+".]"); }
1004 assertEquals("conversion to XML and back should preserve equality",
1005 ad,
1006 AccessionData.parseFromXML(xml));
1007 }
1008 }
1009
1010 /**On-the-wire format of Thumbnail. */
1011 private static final byte serData_Thumbnail_20060704[/*199*/] = {
1012 -84, -19, 0, 5, 115, 114, 0, 49, // ....sr.1
1013 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1014 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1015 114, 67, 111, 114, 101, 46, 69, 120, // rCore.Ex
1016 104, 105, 98, 105, 116, 84, 104, 117, // hibitThu
1017 109, 98, 110, 97, 105, 108, 115, 36, // mbnails$
1018 84, 104, 117, 109, 98, 110, 97, 105, // Thumbnai
1019 108, -18, 63, -36, 92, 71, 75, 76, // l.?.\GKL
1020 -46, 2, 0, 2, 91, 0, 4, 100, // ....[..d
1021 97, 116, 97, 116, 0, 2, 91, 66, // atat..[B
1022 76, 0, 5, 120, 121, 68, 105, 109, // L..xyDim
1023 116, 0, 20, 76, 106, 97, 118, 97, // t..Ljava
1024 47, 97, 119, 116, 47, 68, 105, 109, // /awt/Dim
1025 101, 110, 115, 105, 111, 110, 59, 120, // ension;x
1026 112, 117, 114, 0, 2, 91, 66, -84, // pur..[B.
1027 -13, 23, -8, 6, 8, 84, -32, 2, // .....T..
1028 0, 0, 120, 112, 0, 0, 0, 3, // ..xp....
1029 1, 2, 3, 115, 114, 0, 18, 106, // ...sr..j
1030 97, 118, 97, 46, 97, 119, 116, 46, // ava.awt.
1031 68, 105, 109, 101, 110, 115, 105, 111, // Dimensio
1032 110, 65, -114, -39, -41, -84, 95, 68, // nA...._D
1033 20, 2, 0, 2, 73, 0, 6, 104, // ....I..h
1034 101, 105, 103, 104, 116, 73, 0, 5, // eightI..
1035 119, 105, 100, 116, 104, 120, 112, 0, // widthxp.
1036 0, 0, 4, 0, 0, 0, 3, // .......
1037 };
1038
1039 /**Test that we can (de)serialise Thumbnail data OK. */
1040 public static void testThumbnailSer()
1041 throws Exception
1042 {
1043 //assertImmutable(ExhibitThumbnails.Thumbnail.class);
1044
1045 final Dimension dim1 = new Dimension(3, 4);
1046 final ExhibitThumbnails.Thumbnail tn1 =
1047 new ExhibitThumbnails.Thumbnail(new byte[]{1,2,3}, dim1);
1048 checkSerialisationPreservesEquality(tn1);
1049
1050 final ExhibitThumbnails.Thumbnail tn1Frozen20060704 = (ExhibitThumbnails.Thumbnail)
1051 deserialiseFromByteArray(serData_Thumbnail_20060704);
1052 assertEquals("Must preserve equality when deserialised", tn1, tn1Frozen20060704);
1053 }
1054
1055 /**Test the robustness of our decompression routines in conjunction with deserialization.
1056 * We expect clean failures rather than hangs or messy crashes,
1057 * whatever junk is thrown at the decompressors.
1058 */
1059 public static void testDecompDeserRobustness()
1060 {
1061 // We construct various badly-broken samples of input data.
1062 final byte randomRubbish[] = new byte[rnd.nextInt(1<<18)];
1063 rnd.nextBytes(randomRubbish);
1064 final byte[][] testData = {
1065 new byte[0] /* Empty. */ ,
1066 new byte[1] /* Impossible/invalid single zero byte for compressed results. */ ,
1067 randomRubbish
1068 };
1069
1070 for(final byte[] data : testData)
1071 {
1072 for(final CompressionLevel cl : CompressionLevel.values())
1073 {
1074 if(cl.getLevel() > GenUtils.MAX_SUPPORTED_COMPRESSION_LEVEL.getLevel())
1075 {
1076 Main.getOut().println("[Skipping test on unsupported compression level "+cl+"...]");
1077 continue;
1078 }
1079 try
1080 {
1081 final ByteArrayInputStream bais = new ByteArrayInputStream(data);
1082 final InputStream wis = GenUtils.wrapForDecompression(bais, cl);
1083 final ObjectInputStream ois = new ObjectInputStream(wis);
1084 ois.readObject();
1085 }
1086 catch(final IOException e)
1087 {
1088 // IOException is OK in face of garbage input.
1089 }
1090 catch(final ClassNotFoundException e)
1091 {
1092 // ClassNotFoundException is OK though rare in face of garbage input.
1093 }
1094 }
1095 }
1096 }
1097
1098 /**Empty AEP delta instance. */
1099 private static final byte serData_AllExhibitPropertiesDelta_empty[/*408*/] = {
1100 -84, -19, 0, 5, 115, 114, 0, 47, // ....sr./
1101 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1102 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1103 114, 67, 111, 114, 101, 46, 65, 108, // rCore.Al
1104 108, 69, 120, 104, 105, 98, 105, 116, // lExhibit
1105 80, 114, 111, 112, 101, 114, 116, 105, // Properti
1106 101, 115, 68, 101, 108, 116, 97, 119, // esDeltaw
1107 118, -113, 43, 63, 120, 56, 79, 3, // v.+?x8O.
1108 0, 8, 74, 0, 24, 104, 97, 115, // ..J..has
1109 104, 78, 111, 116, 67, 104, 97, 110, // hNotChan
1110 103, 101, 100, 83, 105, 110, 99, 101, // gedSince
1111 65, 102, 116, 101, 114, 73, 0, 15, // AfterI..
1112 108, 101, 110, 103, 116, 104, 65, 69, // lengthAE
1113 73, 68, 65, 102, 116, 101, 114, 73, // IDAfterI
1114 0, 16, 108, 101, 110, 103, 116, 104, // ..length
1115 65, 69, 73, 68, 66, 101, 102, 111, // AEIDBefo
1116 114, 101, 74, 0, 16, 108, 111, 110, // reJ..lon
1117 103, 72, 97, 115, 104, 65, 69, 80, // gHashAEP
1118 65, 102, 116, 101, 114, 74, 0, 17, // AfterJ..
1119 108, 111, 110, 103, 72, 97, 115, 104, // longHash
1120 65, 69, 80, 66, 101, 102, 111, 114, // AEPBefor
1121 101, 74, 0, 18, 116, 105, 109, 101, // eJ..time
1122 115, 116, 97, 109, 112, 65, 69, 73, // stampAEI
1123 68, 65, 102, 116, 101, 114, 74, 0, // DAfterJ.
1124 19, 116, 105, 109, 101, 115, 116, 97, // .timesta
1125 109, 112, 65, 69, 73, 68, 66, 101, // mpAEIDBe
1126 102, 111, 114, 101, 76, 0, 9, 101, // foreL..e
1127 112, 103, 105, 65, 102, 116, 101, 114, // pgiAfter
1128 116, 0, 51, 76, 111, 114, 103, 47, // t.3Lorg/
1129 104, 100, 47, 100, 47, 112, 103, 50, // hd/d/pg2
1130 107, 47, 115, 118, 114, 67, 111, 114, // k/svrCor
1131 101, 47, 69, 120, 104, 105, 98, 105, // e/Exhibi
1132 116, 80, 114, 111, 112, 115, 71, 108, // tPropsGl
1133 111, 98, 97, 108, 73, 109, 109, 117, // obalImmu
1134 116, 97, 98, 108, 101, 59, 120, 112, // table;xp
1135 0, 0, 0, 0, 0, 0, 0, 0, // ........
1136 0, 0, 0, 0, 0, 0, 0, 0, // ........
1137 0, 0, 0, 0, 0, 0, 0, 0, // ........
1138 0, 0, 0, 0, 0, 0, 0, 0, // ........
1139 0, 0, 0, 0, 0, 0, 0, 0, // ........
1140 0, 0, 0, 0, 0, 0, 0, 0, // ........
1141 112, 117, 114, 0, 57, 91, 76, 111, // pur.9[Lo
1142 114, 103, 46, 104, 100, 46, 100, 46, // rg.hd.d.
1143 112, 103, 50, 107, 46, 115, 118, 114, // pg2k.svr
1144 67, 111, 114, 101, 46, 65, 108, 108, // Core.All
1145 69, 120, 104, 105, 98, 105, 116, 80, // ExhibitP
1146 114, 111, 112, 101, 114, 116, 105, 101, // ropertie
1147 115, 68, 101, 108, 116, 97, 36, 67, // sDelta$C
1148 104, 97, 110, 103, 101, 59, -39, -39, // hange;..
1149 14, -36, 97, 70, -99, -85, 2, 0, // ..aF....
1150 0, 120, 112, 0, 0, 0, 0, 120, // .xp....x
1151 };
1152
1153 /**Simple AllExhibitPropertiesDelta (de)serialisation tests.
1154 * While it is not promised that the serialised format for
1155 * AllExhibitPropertiesDelta will be stable indefinitely,
1156 * we want to make sure that we don't change it inadvertently,
1157 * which would prevent clients and servers exchanging it for example.
1158 */
1159 public static void testSerAllExhibitPropertiesDelta()
1160 throws Exception
1161 {
1162 final AllExhibitPropertiesDelta empty = new AllExhibitPropertiesDelta();
1163 checkObjectCanBeSerialisedAndDeserialised(empty);
1164
1165 // Check that we can deserialise a frozen instance.
1166 final AllExhibitPropertiesDelta aepd1 = (AllExhibitPropertiesDelta)
1167 deserialiseFromByteArray(serData_AllExhibitPropertiesDelta_empty);
1168 // Test sample field(s).
1169 assertEquals("Fields must have correct value", 0, aepd1.hashNotChangedSinceAfter);
1170 assertEquals("Fields must have correct value", 0, aepd1.lengthAEIDBefore);
1171 assertEquals("Fields must have correct value", 0, aepd1.lengthAEIDAfter);
1172 }
1173
1174 /**Sample input String values for Compact7BitString tests.
1175 * These values include hard/edge cases and realistic sample data.
1176 * <p>
1177 * These are tested individually and all together in one stream.
1178 */
1179 private static final String[] TEST_C7BS_DATA =
1180 {
1181 "prefix1 prefix1suffix prefix2 prefix2suffix prefix2suffix" /* Frozen in serialsed test cases. Hard case for partial token matching. */,
1182 "" /* The empty string is one edge case. */,
1183 "\0" /* A single null/control character is potentially tricky. */,
1184 "abc abc" /* Short text with one repeat. */,
1185 "abc abc abc abc abc abc" /* Short text with some repeats. */,
1186 "Repeated internal sequences\r\n\r\nof various sorts.\r\nBanana, banadana?\r\n------\r\n 0, 0, 0, 0, 0, 0, 0, 0, // ........",
1187 "compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression compression" /* Highly compressable text. */,
1188 "Some simple English text.",
1189 "Some simple English text\r\ncontaining line-\r\nbreaks and\t\ttabs but no easy repeats.",
1190 "<sample><tag name=\"X Resolution\" value=\"72 dots per inch\"/><tag name=\"t2\" value=\"3.141\"/><tag name=\"Y Resolution\" value=\"72 dots per inch\"/><tag name=\"Exposure Program\" value=\"Program normal\"></sample>",
1191 "<sample><tag name=\"X Resolution\" value=\"72 dots per inch\"/><tag name=\"t2\" value=\"2.1828\"/><tag name=\"Y Resolution\" value=\"72 dots per inch\"/><tag name=\"Exposure Program\" value=\"Program normal\"></sample>",
1192 "<p class=MsoNormal><span lang=EN-US style='mso-ansi-language:EN-US'>Simp<o:p></o:p></span></p><p class=MsoNormal><span style='font-family:Arial'>Asia Pac 亚 太 区<o:p></o:p></span></p><p class=MsoNormal><span style='font-family:Arial'><![if !supportEmptyParas]> <![endif]><o:p></o:p></span></p>",
1193 "<h2>Metadata</h2><dl compact><dt><b>metadata</b><dd><dl compact><dt><b>image</b><dd><dl compact><dt><b>javax_imageio_1.0</b><dd><dl compact><dt><b>Chroma</b><dd><dl compact><dt><b>ColorSpaceType</b> name=\"YCbCr\"</dl><dl compact><dt><b>NumChannels</b> value=\"3\"</dl></dl><dl compact><dt><b>Compression</b><dd><dl compact><dt><b>CompressionTypeName</b> value=\"JPEG\"</dl><dl compact><dt><b>Lossless</b> value=\"false\"</dl><dl compact><dt><b>NumProgressiveScans</b> value=\"1\"</dl></dl><dl compact><dt><b>Dimension</b><dd><dl compact><dt><b>ImageOrientation</b> value=\"normal\"</dl></dl></dl></dl><dl compact><dt><b>native</b><dd><dl compact><dt><b>EXIF</b><dd><dl compact><dt><b>tag</b> name=\"Make\" value=\"SONY\"</dl><dl compact><dt><b>tag</b> name=\"Model\" value=\"DSC-F828\"</dl><dl compact><dt><b>tag</b> name=\"Orientation\" value=\"top, left side\"</dl><dl compact><dt><b>tag</b> name=\"X Resolution\" value=\"72 dots per inch\"</dl><dl compact><dt><b>tag</b> name=\"Y Resolution\" value=\"72 dots per inch\"</dl><dl compact><dt><b>tag</b> name=\"Resolution Unit\" value=\"Inch\"</dl><dl compact><dt><b>tag</b> name=\"Date/Time\" value=\"2005:11:22 13:35:58\"</dl><dl compact><dt><b>tag</b> name=\"YCbCr Positioning\" value=\"Datum point\"</dl><dl compact><dt><b>tag</b> name=\"Exposure Time\" value=\"1/60 sec\"</dl><dl compact><dt><b>tag</b> name=\"F-Number\" value=\"F3.2\"</dl><dl compact><dt><b>tag</b> name=\"Exposure Program\" value=\"Program normal\"</dl><dl compact><dt><b>tag</b> name=\"ISO Speed Ratings\" value=\"64\"</dl><dl compact><dt><b>tag</b> name=\"Exif Version\" value=\"2.20\"</dl><dl compact><dt><b>tag</b> name=\"Date/Time Original\" value=\"2005:11:22 13:35:58\"</dl><dl compact><dt><b>tag</b> name=\"Date/Time Digitized\" value=\"2005:11:22 13:35:58\"</dl><dl compact><dt><b>tag</b> name=\"Components Configuration\" value=\"YCbCr\"</dl><dl compact><dt><b>tag</b> name=\"Compressed Bits Per Pixel\" value=\"8 bits/pixel\"</dl><dl compact><dt><b>tag</b> name=\"Exposure Bias Value\" value=\"0\"</dl><dl compact><dt><b>tag</b> name=\"Max Aperture Value\" value=\"F2\"</dl><dl compact><dt><b>tag</b> name=\"Metering Mode\" value=\"Center weighted average\"</dl><dl compact><dt><b>tag</b> name=\"Light Source\" value=\"Unknown\"</dl><dl compact><dt><b>tag</b> name=\"Flash\" value=\"Unknown (16)\"</dl><dl compact><dt><b>tag</b> name=\"Focal Length\" value=\"9.5 mm\"</dl><dl compact><dt><b>tag</b> name=\"FlashPix Version\" value=\"1.00\"</dl><dl compact><dt><b>tag</b> name=\"Color Space\" value=\"sRGB\"</dl><dl compact><dt><b>tag</b> name=\"Exif Image Width\" value=\"3264 pixels\"</dl><dl compact><dt><b>tag</b> name=\"Exif Image Height\" value=\"2448 pixels\"</dl><dl compact><dt><b>tag</b> name=\"File Source\" value=\"Digital Still Camera (DSC)\"</dl><dl compact><dt><b>tag</b> name=\"Scene Type\" value=\"Directly photographed image\"</dl><dl compact><dt><b>tag</b> name=\"Unknown tag (0xa401)\" value=\"0\"</dl><dl compact><dt><b>tag</b> name=\"Unknown tag (0xa402)\" value=\"0\"</dl><dl compact><dt><b>tag</b> name=\"Unknown tag (0xa403)\" value=\"0\"</dl><dl compact><dt><b>tag</b> name=\"Unknown tag (0xa406)\" value=\"0\"</dl><dl compact><dt><b>tag</b> name=\"Unknown tag (0xa408)\" value=\"0\"</dl><dl compact><dt><b>tag</b> name=\"Unknown tag (0xa409)\" value=\"0\"</dl><dl compact><dt><b>tag</b> name=\"Unknown tag (0xa40a)\" value=\"0\"</dl><dl compact><dt><b>tag</b> name=\"Unknown tag (0xc4a5)\" value=\"PrintIM\"</dl><dl compact><dt><b>tag</b> name=\"Compression\" value=\"JPEG compression\"</dl><dl compact><dt><b>tag</b> name=\"Thumbnail Offset\" value=\"2486 bytes\"</dl><dl compact><dt><b>tag</b> name=\"Thumbnail Length\" value=\"9657 bytes\"</dl><dl compact><dt><b>tag</b> name=\"Thumbnail Data\" value=\"[9657 bytes of thumbnail data]\"</dl></dl></dl></dl><dl compact><dt><b>accessionData</b><dd><dl compact><dt><b>date</b> value=\"1132678851000\"</dl><dl compact><dt><b>size</b> value=\"3464887\"</dl><dl compact><dt><b>hash-CRC32</b> value=\"e697175\"</dl><dl compact><dt><b>hash-MD5</b> value=\"2fc4e2c7fd8ea3047d6bf9ae22df8f44\"</dl></dl>",
1194 "The Indian way apparently takes years of practice, whereas for the Irish version one may only need strong booze... Arrived over the Net by email.",
1195 "Even the drawstrings [draw-strings] are quite interesting in close-up [closeup].",
1196 "Hacı Bektaş-ı Veli is a famous 13th century Islamic mystic, philosopher and dervish. His tomb and monestary are in the town of Hacibektaş, which is named after him. Hacı Bektaş-ı Veli was born and educated in Khorasan (north-east Iran); he was the grandson of Sheik Ahmet. After his pilgrimage to Mecca, he travelled around the middle east, finally coming to Anatolia in a period of political and economic turmoil. Wishing to help restore unity he settled here and developed and taught his own approach to being a pure Moslem. This approach was based on four tenets: love of God (the only way to attain maturity and peace); focus on substance rather than on the superficial (e.g. God does not see your actions, he sees your intentions); love and unity among people (the only things that illuminate your way are devine and human love); and the power of sanctity (you should seek God within, not externally). He particularly taught tolerance and equality for all through love, and at it's heart his philosophy incorporates the same substance as the Universal Declaration of Human Rights of 1948. Through the work of Hacı Bektaş-ı Veli unity was restored to the Turks of Anatolia, and the Turkish language became the language of scholarship and law (instead of Arabic or Persian).<BR> The interior of Hacı Bektaş-ı Veli's tomb chamber is an interesting shape. At the bottom the room is square; higher up it is octagonal; and at the top there is a circular dome. This is essentially the same shape as the Pharos, the ancient lighthouse at Alexandria, Egypt. I've seen this shape in other places too, but I don't know whether the Pharos was the original use of this shape, or whether the shape is older than that. I'm certain that there must be some meaning behind the shape, but I'm afraid I don't know it.",
1197 "<image><javax_imageio_1.0><Chroma><ColorSpaceType name=\"RGB\"/><NumChannels value=\"4\"/><Gamma value=\"0.45453998\"/><BlackIsZero value=\"true\"/><Palette><PaletteEntry alpha=\"255\" blue=\"255\" green=\"255\" index=\"0\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"255\" index=\"1\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"255\" index=\"2\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"255\" index=\"3\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"255\" index=\"4\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"255\" index=\"5\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"204\" index=\"6\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"204\" index=\"7\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"204\" index=\"8\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"204\" index=\"9\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"204\" index=\"10\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"204\" index=\"11\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"153\" index=\"12\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"153\" index=\"13\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"153\" index=\"14\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"153\" index=\"15\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"153\" index=\"16\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"153\" index=\"17\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"102\" index=\"18\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"102\" index=\"19\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"102\" index=\"20\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"102\" index=\"21\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"102\" index=\"22\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"102\" index=\"23\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"51\" index=\"24\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"51\" index=\"25\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"51\" index=\"26\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"51\" index=\"27\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"51\" index=\"28\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"51\" index=\"29\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"0\" index=\"30\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"0\" index=\"31\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"0\" index=\"32\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"0\" index=\"33\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"0\" index=\"34\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"0\" index=\"35\" red=\"255\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"255\" index=\"36\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"255\" index=\"37\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"255\" index=\"38\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"255\" index=\"39\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"255\" index=\"40\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"255\" index=\"41\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"204\" index=\"42\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"204\" index=\"43\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"204\" index=\"44\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"204\" index=\"45\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"204\" index=\"46\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"204\" index=\"47\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"153\" index=\"48\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"153\" index=\"49\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"153\" index=\"50\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"153\" index=\"51\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"153\" index=\"52\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"153\" index=\"53\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"102\" index=\"54\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"102\" index=\"55\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"102\" index=\"56\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"102\" index=\"57\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"102\" index=\"58\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"102\" index=\"59\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"51\" index=\"60\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"51\" index=\"61\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"51\" index=\"62\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"51\" index=\"63\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"51\" index=\"64\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"51\" index=\"65\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"0\" index=\"66\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"0\" index=\"67\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"0\" index=\"68\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"0\" index=\"69\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"0\" index=\"70\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"0\" index=\"71\" red=\"204\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"255\" index=\"72\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"255\" index=\"73\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"255\" index=\"74\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"255\" index=\"75\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"255\" index=\"76\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"255\" index=\"77\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"204\" index=\"78\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"204\" index=\"79\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"204\" index=\"80\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"204\" index=\"81\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"204\" index=\"82\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"204\" index=\"83\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"153\" index=\"84\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"153\" index=\"85\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"153\" index=\"86\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"153\" index=\"87\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"153\" index=\"88\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"153\" index=\"89\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"102\" index=\"90\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"102\" index=\"91\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"102\" index=\"92\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"102\" index=\"93\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"102\" index=\"94\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"102\" index=\"95\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"51\" index=\"96\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"51\" index=\"97\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"51\" index=\"98\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"51\" index=\"99\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"51\" index=\"100\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"51\" index=\"101\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"0\" index=\"102\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"0\" index=\"103\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"0\" index=\"104\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"0\" index=\"105\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"0\" index=\"106\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"0\" index=\"107\" red=\"153\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"255\" index=\"108\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"255\" index=\"109\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"255\" index=\"110\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"255\" index=\"111\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"255\" index=\"112\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"255\" index=\"113\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"204\" index=\"114\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"204\" index=\"115\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"204\" index=\"116\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"204\" index=\"117\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"204\" index=\"118\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"204\" index=\"119\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"153\" index=\"120\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"153\" index=\"121\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"153\" index=\"122\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"153\" index=\"123\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"153\" index=\"124\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"153\" index=\"125\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"102\" index=\"126\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"102\" index=\"127\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"102\" index=\"128\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"102\" index=\"129\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"102\" index=\"130\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"102\" index=\"131\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"51\" index=\"132\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"51\" index=\"133\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"51\" index=\"134\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"51\" index=\"135\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"51\" index=\"136\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"51\" index=\"137\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"0\" index=\"138\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"0\" index=\"139\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"0\" index=\"140\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"0\" index=\"141\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"0\" index=\"142\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"0\" index=\"143\" red=\"102\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"255\" index=\"144\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"255\" index=\"145\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"255\" index=\"146\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"255\" index=\"147\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"255\" index=\"148\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"255\" index=\"149\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"204\" index=\"150\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"204\" index=\"151\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"204\" index=\"152\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"204\" index=\"153\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"204\" index=\"154\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"204\" index=\"155\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"153\" index=\"156\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"153\" index=\"157\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"153\" index=\"158\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"153\" index=\"159\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"153\" index=\"160\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"153\" index=\"161\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"102\" index=\"162\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"102\" index=\"163\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"102\" index=\"164\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"102\" index=\"165\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"102\" index=\"166\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"102\" index=\"167\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"51\" index=\"168\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"51\" index=\"169\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"51\" index=\"170\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"51\" index=\"171\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"51\" index=\"172\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"51\" index=\"173\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"0\" index=\"174\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"0\" index=\"175\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"0\" index=\"176\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"0\" index=\"177\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"0\" index=\"178\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"0\" index=\"179\" red=\"51\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"255\" index=\"180\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"255\" index=\"181\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"255\" index=\"182\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"255\" index=\"183\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"255\" index=\"184\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"255\" index=\"185\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"204\" index=\"186\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"204\" index=\"187\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"204\" index=\"188\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"204\" index=\"189\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"204\" index=\"190\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"204\" index=\"191\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"153\" index=\"192\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"153\" index=\"193\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"153\" index=\"194\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"153\" index=\"195\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"153\" index=\"196\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"153\" index=\"197\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"102\" index=\"198\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"102\" index=\"199\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"102\" index=\"200\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"102\" index=\"201\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"102\" index=\"202\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"102\" index=\"203\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"51\" index=\"204\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"51\" index=\"205\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"51\" index=\"206\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"51\" index=\"207\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"51\" index=\"208\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"51\" index=\"209\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"0\" index=\"210\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"204\" green=\"0\" index=\"211\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"153\" green=\"0\" index=\"212\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"102\" green=\"0\" index=\"213\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"51\" green=\"0\" index=\"214\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"0\" green=\"0\" index=\"215\" red=\"0\"/><PaletteEntry alpha=\"255\" blue=\"255\" green=\"254\" index=\"216\" red=\"254\"/><PaletteEntry alpha=\"255\" blue=\"7\" green=\"6\" index=\"217\" red=\"6\"/><PaletteEntry alpha=\"255\" blue=\"1\" green=\"6\" index=\"218\" red=\"6\"/><PaletteEntry alpha=\"255\" blue=\"1\" green=\"14\" index=\"219\" red=\"2\"/><PaletteEntry alpha=\"255\" blue=\"46\" green=\"15\" index=\"220\" red=\"46\"/><PaletteEntry alpha=\"255\" blue=\"201\" green=\"184\" index=\"221\" red=\"201\"/><PaletteEntry alpha=\"255\" blue=\"72\" green=\"51\" index=\"222\" red=\"69\"/><PaletteEntry alpha=\"255\" blue=\"232\" green=\"219\" index=\"223\" red=\"221\"/><PaletteEntry alpha=\"255\" blue=\"78\" green=\"16\" index=\"224\" red=\"11\"/><PaletteEntry alpha=\"255\" blue=\"46\" green=\"13\" index=\"225\" red=\"10\"/><PaletteEntry alpha=\"255\" blue=\"78\" green=\"39\" index=\"226\" red=\"36\"/><PaletteEntry alpha=\"255\" blue=\"91\" green=\"81\" index=\"227\" red=\"79\"/><PaletteEntry alpha=\"255\" blue=\"201\" green=\"183\" index=\"228\" red=\"179\"/><PaletteEntry alpha=\"255\" blue=\"24\" green=\"13\" index=\"229\" red=\"10\"/><PaletteEntry alpha=\"255\" blue=\"127\" green=\"83\" index=\"230\" red=\"71\"/><PaletteEntry alpha=\"255\" blue=\"217\" green=\"210\" index=\"231\" red=\"208\"/><PaletteEntry alpha=\"255\" blue=\"141\" green=\"136\" index=\"232\" red=\"132\"/><PaletteEntry alpha=\"255\" blue=\"178\" green=\"171\" index=\"233\" red=\"165\"/><PaletteEntry alpha=\"255\" blue=\"248\" green=\"247\" index=\"234\" red=\"246\"/><PaletteEntry alpha=\"255\" blue=\"173\" green=\"153\" index=\"235\" red=\"132\"/><PaletteEntry alpha=\"255\" blue=\"92\" green=\"70\" index=\"236\" red=\"33\"/><PaletteEntry alpha=\"255\" blue=\"207\" green=\"200\" index=\"237\" red=\"184\"/><PaletteEntry alpha=\"255\" blue=\"47\" green=\"43\" index=\"238\" red=\"14\"/><PaletteEntry alpha=\"255\" blue=\"42\" green=\"74\" index=\"239\" red=\"38\"/><PaletteEntry alpha=\"255\" blue=\"13\" green=\"42\" index=\"240\" red=\"12\"/><PaletteEntry alpha=\"255\" blue=\"8\" green=\"24\" index=\"241\" red=\"9\"/><PaletteEntry alpha=\"255\" blue=\"113\" green=\"129\" index=\"242\" red=\"125\"/><PaletteEntry alpha=\"255\" blue=\"184\" green=\"201\" index=\"243\" red=\"200\"/><PaletteEntry alpha=\"255\" blue=\"15\" green=\"45\" index=\"244\" red=\"45\"/><PaletteEntry alpha=\"255\" blue=\"38\" green=\"69\" index=\"245\" red=\"82\"/><PaletteEntry alpha=\"255\" blue=\"117\" green=\"151\" index=\"246\" red=\"175\"/><PaletteEntry alpha=\"255\" blue=\"148\" green=\"162\" index=\"247\" red=\"172\"/><PaletteEntry alpha=\"255\" blue=\"178\" green=\"181\" index=\"248\" red=\"184\"/><PaletteEntry alpha=\"255\" blue=\"19\" green=\"29\" index=\"249\" red=\"77\"/><PaletteEntry alpha=\"255\" blue=\"181\" green=\"184\" index=\"250\" red=\"201\"/><PaletteEntry alpha=\"255\" blue=\"8\" green=\"9\" index=\"251\" red=\"229\"/><PaletteEntry alpha=\"255\" blue=\"11\" green=\"11\" index=\"252\" red=\"45\"/><PaletteEntry alpha=\"255\" blue=\"8\" green=\"8\" index=\"253\" red=\"24\"/><PaletteEntry alpha=\"255\" blue=\"8\" green=\"8\" index=\"254\" red=\"8\"/><PaletteEntry alpha=\"0\" blue=\"255\" green=\"255\" index=\"255\" red=\"255\"/></Palette></Chroma><Compression><CompressionTypeName value=\"deflate\"/><Lossless value=\"true\"/><NumProgressiveScans value=\"1\"/></Compression><Data><PlanarConfiguration value=\"PixelInterleaved\"/><SampleFormat value=\"Index\"/><BitsPerSample value=\"8 8 8 8\"/></Data><Dimension><PixelAspectRatio value=\"1.0\"/><ImageOrientation value=\"Normal\"/><HorizontalPixelSize value=\"0.35273367\"/><VerticalPixelSize value=\"0.35273367\"/></Dimension><Transparency><Alpha value=\"nonpremultipled\"/><TransparentColor/></Transparency></javax_imageio_1.0></image>",
1198 "<image><javax_imageio_1.0><Chroma><ColorSpaceType name=\"YCbCr\"/><NumChannels value=\"3\"/></Chroma><Compression><CompressionTypeName value=\"JPEG\"/><Lossless value=\"false\"/><NumProgressiveScans value=\"1\"/></Compression><Dimension><PixelAspectRatio value=\"1.0\"/><ImageOrientation value=\"normal\"/><HorizontalPixelSize value=\"0.254\"/><VerticalPixelSize value=\"0.254\"/></Dimension></javax_imageio_1.0></image>",
1199 "<image><javax_imageio_1.0><Chroma><ColorSpaceType name=\"YCbCr\"/><NumChannels value=\"3\"/></Chroma><Compression><CompressionTypeName value=\"JPEG\"/><Lossless value=\"false\"/><NumProgressiveScans value=\"1\"/></Compression><Dimension><ImageOrientation value=\"normal\"/></Dimension></javax_imageio_1.0></image><native><EXIF><tag name=\"Color Space\" value=\"sRGB\"/><tag name=\"Components Configuration\" value=\"YCbCr\"/><tag name=\"Compressed Bits Per Pixel\" value=\"8 bits/pixel\"/><tag name=\"Compression\" value=\"JPEG (old-style)\"/><tag name=\"Contrast\" value=\"None\"/><tag name=\"Custom Rendered\" value=\"Normal process\"/><tag name=\"Date/Time\" value=\"2005:04:18 17:19:32\"/><tag name=\"Date/Time Digitized\" value=\"2005:04:18 17:19:32\"/><tag name=\"Date/Time Original\" value=\"2005:04:18 17:19:32\"/><tag name=\"Exif Image Height\" value=\"480 pixels\"/><tag name=\"Exif Image Width\" value=\"640 pixels\"/><tag name=\"Exif Version\" value=\"2.20\"/><tag name=\"Exposure Bias Value\" value=\"0 EV\"/><tag name=\"Exposure Mode\" value=\"Auto exposure\"/><tag name=\"Exposure Program\" value=\"Program normal\"/><tag name=\"Exposure Time\" value=\"1/60 sec\"/><tag name=\"F-Number\" value=\"F4\"/><tag name=\"File Source\" value=\"Digital Still Camera (DSC)\"/><tag name=\"Flash\" value=\"Flash did not fire, auto\"/><tag name=\"FlashPix Version\" value=\"1.00\"/><tag name=\"Focal Length\" value=\"7.1 mm\"/><tag name=\"ISO Speed Ratings\" value=\"64\"/><tag name=\"Light Source\" value=\"Unknown\"/><tag name=\"Make\" value=\"SONY\"/><tag name=\"Max Aperture Value\" value=\"F2\"/><tag name=\"Metering Mode\" value=\"Center weighted average\"/><tag name=\"Model\" value=\"DSC-F828\"/><tag name=\"Orientation\" value=\"Top, left side (Horizontal / normal)\"/><tag name=\"Resolution Unit\" value=\"Inch\"/><tag name=\"Saturation\" value=\"None\"/><tag name=\"Scene Capture Type\" value=\"Standard\"/><tag name=\"Scene Type\" value=\"Directly photographed image\"/><tag name=\"Sharpness\" value=\"None\"/><tag name=\"Thumbnail Data\" value=\"[3071 bytes of thumbnail data]\"/><tag name=\"Thumbnail Length\" value=\"3071 bytes\"/><tag name=\"Thumbnail Offset\" value=\"2486 bytes\"/><tag name=\"Unknown tag (0xc4a5)\" value=\"80 114 105 110 116 73 77 0 48 50 53 48 0 0 2 0 2 0 1 0 0 0 1 1 0 0 0 0\"/><tag name=\"White Balance\" value=\"Auto white balance\"/><tag name=\"X Resolution\" value=\"72 dots per inch\"/><tag name=\"Y Resolution\" value=\"72 dots per inch\"/><tag name=\"YCbCr Positioning\" value=\"Datum point\"/></EXIF></native>",
1200 "<image><javax_imageio_1.0><Chroma><ColorSpaceType name=\"YCbCr\"/><NumChannels value=\"3\"/></Chroma><Compression><CompressionTypeName value=\"JPEG\"/><Lossless value=\"false\"/><NumProgressiveScans value=\"1\"/></Compression><Dimension><ImageOrientation value=\"normal\"/></Dimension></javax_imageio_1.0></image><native><EXIF><tag name=\"Color Space\" value=\"sRGB\"/><tag name=\"Components Configuration\" value=\"YCbCr\"/><tag name=\"Compression\" value=\"JPEG (old-style)\"/><tag name=\"Contrast\" value=\"None\"/><tag name=\"Custom Rendered\" value=\"Normal process\"/><tag name=\"Date/Time\" value=\"0000:00:00 00:00:00\"/><tag name=\"Date/Time Digitized\" value=\"0000:00:00 00:00:00\"/><tag name=\"Date/Time Original\" value=\"0000:00:00 00:00:00\"/><tag name=\"Digital Zoom Ratio\" value=\"1\"/><tag name=\"Exif Image Height\" value=\"1920 pixels\"/><tag name=\"Exif Image Width\" value=\"2560 pixels\"/><tag name=\"Exif Version\" value=\"2.20\"/><tag name=\"Exposure Bias Value\" value=\"0.3 EV\"/><tag name=\"Exposure Mode\" value=\"Manual exposure\"/><tag name=\"Exposure Program\" value=\"Program normal\"/><tag name=\"Exposure Time\" value=\"1/60 sec\"/><tag name=\"F-Number\" value=\"F3.2\"/><tag name=\"File Source\" value=\"Digital Still Camera (DSC)\"/><tag name=\"Flash\" value=\"Flash did not fire, auto\"/><tag name=\"FlashPix Version\" value=\"1.00\"/><tag name=\"Focal Length\" value=\"9.42 mm\"/><tag name=\"Gain Control\" value=\"Low gain down\"/><tag name=\"ISO Speed Ratings\" value=\"320\"/><tag name=\"Image Description\" value=\"OLYMPUS DIGITAL CAMERA \"/><tag name=\"Light Source\" value=\"Unknown\"/><tag name=\"Make\" value=\"OLYMPUS OPTICAL CO.,LTD\"/><tag name=\"Max Aperture Value\" value=\"F2.8\"/><tag name=\"Metering Mode\" value=\"Multi-segment\"/><tag name=\"Model\" value=\"X-2,C-50Z \"/><tag name=\"Orientation\" value=\"Top, left side (Horizontal / normal)\"/><tag name=\"Resolution Unit\" value=\"Inch\"/><tag name=\"Saturation\" value=\"None\"/><tag name=\"Scene Capture Type\" value=\"Standard\"/><tag name=\"Sharpness\" value=\"None\"/><tag name=\"Software\" value=\"28-1008 \"/><tag name=\"Thumbnail Data\" value=\"[6228 bytes of thumbnail data]\"/><tag name=\"Thumbnail Length\" value=\"6228 bytes\"/><tag name=\"Thumbnail Offset\" value=\"7206 bytes\"/><tag name=\"Unknown tag (0xc4a5)\" value=\"80 114 105 110 116 73 77 0 48 50 53 48 0 0 20 0 1 0 20 0 20 0 2 0 1 0 0 0 3 0 -120 0 0 0 7 0 0 0 0 0 8 0 0 0 0 0 9 0 0 0 0 0 10 0 0 0 0 0 11 0 -48 0 0 0 12 0 0 0 0 0 13 0 0 0 0 0 14 0 -24 0 0 0 0 1 1 0 0 0 1 1 -1 0 0 0 2 1 -125 0 0 0 3 1 -128 0 0 0 4 1 -125 0 0 0 5 1 -125 0 0 0 6 1 -125 0 0 0 7 1 -128 -128 -128 0 16 1 -126 0 0 0 9 17 0 0 16 39 0 0 11 15 0 0 16 39 0 0 -105 5 0 0 16 39 0 0 -80 8 0 0 16 39 0 0 1 28 0 0 16 39 0 0 94 2 0 0 16 39 0 0 -117 0 0 0 16 39 0 0 -53 3 0 0 16 39 0 0 -27 27 0 0 16 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\"/><tag name=\"White Balance\" value=\"Auto white balance\"/><tag name=\"X Resolution\" value=\"72 dots per inch\"/><tag name=\"Y Resolution\" value=\"72 dots per inch\"/><tag name=\"YCbCr Positioning\" value=\"Datum point\"/></EXIF></native>",
1201 "I.A.N.A. is as I.A.N.A. does..." /* Repeated 1-char token sequences. */,
1202 "<dl compact><dt><b>metadata</b><dd><dl compact><dt><b>image</b><dd><dl compact><dt><b>javax_imageio_1.0</b><dd><dl compact><dt><b>Chroma</b><dd><dl compact><dt><b>ColorSpaceType</b> name=\"YCbCr\"</dl><dl compact><dt><b>NumChannels</b> value=\"3\"</dl></dl><dl compact><dt><b>Compression</b><dd><dl compact><dt><b>CompressionTypeName</b> value=\"JPEG\"</dl><dl compact><dt><b>Lossless</b> value=\"false\"</dl><dl compact><dt><b>NumProgressiveScans</b> value=\"1\"</dl></dl><dl compact><dt><b>Dimension</b><dd><dl compact><dt><b>PixelAspectRatio</b> value=\"1.0\"</dl><dl compact><dt><b>ImageOrientation</b> value=\"normal\"</dl><dl compact><dt><b>HorizontalPixelSize</b> value=\"0.26458332\"</dl><dl compact><dt><b>VerticalPixelSize</b> value=\"0.26458332\"</dl></dl></dl></dl><dl compact><dt><b>native</b><dd><dl compact><dt><b>EXIF</b><dd><dl compact><dt><b>tag</b> name=\"Color Space\" value=\"sRGB\"</dl><dl compact><dt><b>tag</b> name=\"Components Configuration\" value=\"YCbCr\"</dl><dl compact><dt><b>tag</b> name=\"Compressed Bits Per Pixel\" value=\"4 bits/pixel\"</dl><dl compact><dt><b>tag</b> name=\"Compression\" value=\"JPEG (old-style)\"</dl><dl compact><dt><b>tag</b> name=\"Custom Rendered\" value=\"Normal process\"</dl><dl compact><dt><b>tag</b> name=\"Date/Time\" value=\"2006:01:15 15:38:56\"</dl><dl compact><dt><b>tag</b> name=\"Date/Time Digitized\" value=\"2006:01:15 15:38:56\"</dl><dl compact><dt><b>tag</b> name=\"Date/Time Original\" value=\"2006:01:15 15:38:56\"</dl><dl compact><dt><b>tag</b> name=\"Exif Image Height\" value=\"960 pixels\"</dl><dl compact><dt><b>tag</b> name=\"Exif Image Width\" value=\"1280 pixels\"</dl><dl compact><dt><b>tag</b> name=\"Exif Version\" value=\"2.20\"</dl><dl compact><dt><b>tag</b> name=\"Exposure Bias Value\" value=\"0 EV\"</dl><dl compact><dt><b>tag</b> name=\"Exposure Mode\" value=\"Auto exposure\"</dl><dl compact><dt><b>tag</b> name=\"Exposure Program\" value=\"Program normal\"</dl><dl compact><dt><b>tag</b> name=\"Exposure Time\" value=\"1/200 sec\"</dl><dl compact><dt><b>tag</b> name=\"F-Number\" value=\"F5.2\"</dl><dl compact><dt><b>tag</b> name=\"File Source\" value=\"Digital Still Camera (DSC)\"</dl><dl compact><dt><b>tag</b> name=\"Flash\" value=\"Flash did not fire\"</dl><dl compact><dt><b>tag</b> name=\"FlashPix Version\" value=\"1.00\"</dl><dl compact><dt><b>tag</b> name=\"Focal Length\" value=\"23.7 mm\"</dl><dl compact><dt><b>tag</b> name=\"ISO Speed Ratings\" value=\"100\"</dl><dl compact><dt><b>tag</b> name=\"Light Source\" value=\"Unknown\"</dl><dl compact><dt><b>tag</b> name=\"Make\" value=\"SONY\"</dl><dl compact><dt><b>tag</b> name=\"Max Aperture Value\" value=\"F2.8\"</dl><dl compact><dt><b>tag</b> name=\"Metering Mode\" value=\"Multi-segment\"</dl><dl compact><dt><b>tag</b> name=\"Model\" value=\"DSC-P10\"</dl><dl compact><dt><b>tag</b> name=\"Orientation\" value=\"Top, left side (Horizontal / normal)\"</dl><dl compact><dt><b>tag</b> name=\"Resolution Unit\" value=\"Inch\"</dl><dl compact><dt><b>tag</b> name=\"Scene Capture Type\" value=\"Standard\"</dl><dl compact><dt><b>tag</b> name=\"Scene Type\" value=\"Directly photographed image\"</dl><dl compact><dt><b>tag</b> name=\"Thumbnail Data\" value=\"[4198 bytes of thumbnail data]\"</dl><dl compact><dt><b>tag</b> name=\"Thumbnail Length\" value=\"4198 bytes\"</dl><dl compact><dt><b>tag</b> name=\"Thumbnail Offset\" value=\"2342 bytes\"</dl><dl compact><dt><b>tag</b> name=\"Unknown tag (0xc4a5)\" value=\"80 114 105 110 116 73 77 0 48 50 53 48 0 0 2 0 2 0 1 0 0 0 1 1 0 0 0 0\"</dl><dl compact><dt><b>tag</b> name=\"White Balance\" value=\"Auto white balance\"</dl><dl compact><dt><b>tag</b> name=\"X Resolution\" value=\"72 dots per inch\"</dl><dl compact><dt><b>tag</b> name=\"Y Resolution\" value=\"72 dots per inch\"</dl><dl compact><dt><b>tag</b> name=\"YCbCr Positioning\" value=\"Datum point\"</dl></dl></dl></dl><dl compact><dt><b>accessionData</b><dd><dl compact><dt><b>date</b> value=\"1155636545000\"</dl><dl compact><dt><b>size</b> value=\"122672\"</dl><dl compact><dt><b>hash-CRC32</b> value=\"6cb4f395\"</dl><dl compact><dt><b>hash-MD5</b> value=\"1a8a965611c8171dca007b13937cd0d8\"</dl></dl>" /* Example HTML form of metadata. */ ,
1203 "<table cellpadding=0 border=1 cols=6 width=396><tr height=64 align=center><td width=64><a href=\"/_c/places-and-sights/_more2002/_more04/Norway-Flam-Myrdal-railway-Flamsbana-Kjosfossen-waterfall-1-BG.jpg.html\"><img src=\"/_tn/sml/places-and-sights/_more2002/_more04/Norway-Flam-Myrdal-railway-Flamsbana-Kjosfossen-waterfall-1-BG.jpg\" border=0 width=43 height=64 alt=\"Norway ~ -1-BG.jpg\"></a></td><td width=64><small>JPEG image<br /><a href=\"/_c/places-and-sights/_more2002/_more04/Norway-Flam-Myrdal-railway-Flamsbana-Kjosfossen-waterfall-tweaked-1-BG.jpg.html\"><i>Norway ~ tweaked 1 BG.jpg</i></a></small></td><td width=64><a href=\"/_c/places-and-sights/_more2002/_more04/Norway-Flam-Myrdal-railway-Flamsbana-Kjosfossen-waterfall-2-BG.jpg.html\"><img src=\"/_tn/sml/places-and-sights/_more2002/_more04/Norway-Flam-Myrdal-railway-Flamsbana-Kjosfossen-waterfall-2-BG.jpg\" border=0 width=64 height=43 alt=\"Norway ~ -2-BG.jpg\"></a></td><td width=64><small>JPEG image<br /><a href=\"/_c/places-and-sights/_more2002/_more04/Norway-Flam-Myrdal-railway-Flamsbana-Kjosfossen-waterfall-tweaked-2-BG.jpg.html\"><i>Norway ~ tweaked 2 BG.jpg</i></a></small></td><td width=64><a href=\"/_c/places-and-sights/_more2002/_more04/Norway-Flam-Myrdal-railway-Flamsbana-Kjosfossen-waterfall-3-BG.jpg.html\"><img src=\"/_tn/sml/places-and-sights/_more2002/_more04/Norway-Flam-Myrdal-railway-Flamsbana-Kjosfossen-waterfall-3-BG.jpg\" border=0 width=43 height=64 alt=\"Norway ~ -3-BG.jpg\"></a></td><td width=64><a href=\"/_c/places-and-sights/_more2002/_more04/Norway-Flam-Myrdal-railway-Flamsbana-Kjosfossen-waterfall-tweaked-3-BG.jpg.html\"><img src=\"/_tn/sml/places-and-sights/_more2002/_more04/Norway-Flam-Myrdal-railway-Flamsbana-Kjosfossen-waterfall-tweaked-3-BG.jpg\" border=0 width=43 height=64 alt=\"Norway ~ -tweaked-3-BG.jpg\"></a></td></tr></table>",
1204 "<table cellpadding=0 border=1 cols=10 width=660><tr height=64 align=center><td width=64><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-1-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-1-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -1-DHD.jpg\"></a></td><td width=64><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-2-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-2-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -2-DHD.jpg\"></a></td><td width=64><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-3-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-3-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -3-DHD.jpg\"></a></td><td width=64><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-4-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-4-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -4-DHD.jpg\"></a></td><td width=64><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-5-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-5-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -5-DHD.jpg\"></a></td><td width=64><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-6-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-6-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -6-DHD.jpg\"></a></td><td width=64><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-7-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-7-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -7-DHD.jpg\"></a></td><td width=64><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-8-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-8-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -8-DHD.jpg\"></a></td><td width=64><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-9-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-9-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -9-DHD.jpg\"></a></td><td width=64><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-10-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-10-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -10-DHD.jpg\"></a></td></tr><tr height=64 align=center><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-11-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-11-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -11-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-12-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-12-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -12-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-13-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-13-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -13-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-14-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-14-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -14-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-15-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-15-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -15-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-16-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-16-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -16-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-17-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-17-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -17-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-18-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-18-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -18-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-19-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-19-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -19-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-20-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-20-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -20-DHD.jpg\"></a></td></tr><tr height=64 align=center><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-21-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-21-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -21-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-22-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-22-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -22-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-23-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-23-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -23-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-24-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-24-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -24-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-25-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-25-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -25-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-26-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-26-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -26-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-27-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-27-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -27-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-28-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-28-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -28-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-29-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-29-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -29-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-30-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-30-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -30-DHD.jpg\"></a></td></tr><tr height=64 align=center><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-31-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-31-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -31-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-32-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-32-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -32-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-33-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-33-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -33-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-34-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-34-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -34-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-35-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-35-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -35-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-36-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-36-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -36-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-37-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-37-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -37-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-38-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-38-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -38-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-39-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-39-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -39-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-40-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-40-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -40-DHD.jpg\"></a></td></tr><tr height=64 align=center><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-41-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-41-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -41-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-42-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-42-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -42-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-43-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-43-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -43-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-44-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-44-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -44-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-45-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-45-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -45-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-46-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-46-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -46-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-47-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-47-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -47-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-48-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-48-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -48-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-49-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-49-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -49-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-50-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-50-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -50-DHD.jpg\"></a></td></tr><tr height=64 align=center><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-51-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-51-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -51-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-52-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-52-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -52-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-53-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-53-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -53-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-54-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-54-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -54-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-55-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-55-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -55-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-56-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-56-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -56-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-57-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-57-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -57-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-58-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-58-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -58-DHD.jpg\"></a></td><td><a href=\"/_c/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-59-DHD.jpg.html\"><img src=\"/_tn/sml/money/_more2004/_more12/sale-sales-posters-in-shop-windows-in-Kingston-upon-Thames-just-after-Christmas-2004-after-a-poor-retail-month-59-DHD.jpg\" border=0 width=64 height=48 alt=\"sale ~ -59-DHD.jpg\"></a></td></tr></table>",
1205 };
1206
1207 /**Forced serialised form to check deserialisation is not broken.
1208 * Encoded value: "prefix1 prefix1suffix prefix2 prefix2suffix prefix2suffix".
1209 */
1210 private static final byte serData_C7BS_20060803[/*109*/] = {
1211 -84, -19, 0, 5, 115, 114, 0, 39, // ....sr.'
1212 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1213 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1214 114, 67, 111, 114, 101, 46, 67, 111, // rCore.Co
1215 109, 112, 97, 99, 116, 55, 66, 105, // mpact7Bi
1216 116, 83, 116, 114, 105, 110, 103, -81, // tString.
1217 2, 23, 50, -72, -120, 34, 44, 3, // ..2..",.
1218 0, 0, 120, 112, 119, 46, 45, 112, // ..xpw.-p
1219 114, 101, 102, 105, 120, 49, 32, 112, // refix1.p
1220 114, 101, 102, 105, 120, 49, 115, 117, // refix1su
1221 102, 102, 105, 120, 32, 112, 114, 101, // ffix.pre
1222 102, 105, 120, 50, 32, 112, 114, 101, // fix2.pre
1223 102, 105, 120, 50, 115, 117, 102, 102, // fix2suff
1224 105, 120, 32, -2, 120, // ix..x
1225 };
1226
1227 /**Forced serialised form after allowing for partial token (prefix) matching.
1228 * Encoded value: "prefix1 prefix1suffix prefix2 prefix2suffix prefix2suffix".
1229 */
1230 private static final byte serData_C7BS_20060804[/*103*/] = {
1231 -84, -19, 0, 5, 115, 114, 0, 39, // ....sr.'
1232 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1233 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1234 114, 67, 111, 114, 101, 46, 67, 111, // rCore.Co
1235 109, 112, 97, 99, 116, 55, 66, 105, // mpact7Bi
1236 116, 83, 116, 114, 105, 110, 103, -81, // tString.
1237 2, 23, 50, -72, -120, 34, 44, 3, // ..2..",.
1238 0, 0, 120, 112, 119, 40, 39, 112, // ..xpw('p
1239 114, 101, 102, 105, 120, 49, 32, -2, // refix1..
1240 115, 117, 102, 102, 105, 120, 32, 112, // suffix.p
1241 114, 101, 102, 105, 120, 50, 32, 112, // refix2.p
1242 114, 101, 102, 105, 120, 50, 115, 117, // refix2su
1243 102, 102, 105, 120, 32, -2, 120, // ffix..x
1244 };
1245
1246 /**Test of serialisation and other behaviour of Compact7BitString.
1247 * The aim is that Compact7BitString should usually be significant smaller than String
1248 * in memory and serialised for 7-bit ASCII text,
1249 * especially when multiple similar values are serialised to a single stream.
1250 * <p>
1251 * We take the (maximally) compressed serialised stream to be our target usage.
1252 */
1253 public static void testCompact7BitString()
1254 throws Exception
1255 {
1256 // Check deserialistion of our frozen values.
1257 // The encoded value should be our first test text.
1258 final Compact7BitString frozen1 = (Compact7BitString) (new ObjectInputStream(new ByteArrayInputStream(serData_C7BS_20060803))).readObject();
1259 assertEquals("must be able to extract correct value from frozen instance",
1260 TEST_C7BS_DATA[0], frozen1.toString());
1261 final Compact7BitString frozen2 = (Compact7BitString) (new ObjectInputStream(new ByteArrayInputStream(serData_C7BS_20060804))).readObject();
1262 assertEquals("must be able to extract correct value from frozen instance",
1263 TEST_C7BS_DATA[0], frozen2.toString());
1264
1265 // Use all the fixed cases in the tests.
1266 final ArrayList<String> testData = new ArrayList<String>(Arrays.asList(TEST_C7BS_DATA));
1267
1268 // Create one nasty-but-legal block of random 7-bit ASCII values.
1269 // This (eventually!) covers all possible legal input values.
1270 final byte[] r = new byte[rnd.nextInt(Short.MAX_VALUE+1)];
1271 rnd.nextBytes(r);
1272 final StringBuffer rsb = new StringBuffer(r.length);
1273 for(final byte b : r) { rsb.append((char) (b & 0x7f)); }
1274 // Add it to the test set.
1275 testData.add(rsb.toString());
1276
1277 // Set of static compression dictionaries that we will test against.
1278 final Compact7BitString.StaticDictionary dicts[] = new Compact7BitString.StaticDictionary[]{
1279 ExhibitPropsComputable.sDict,
1280 ExhibitPropsLoadable.sDict,
1281 WebUtils.sDictMD,
1282 };
1283
1284 final ByteArrayOutputStream allStrings = new ByteArrayOutputStream();
1285 final ObjectOutputStream aSoos = new ObjectOutputStream(allStrings);
1286 final ByteArrayOutputStream allC7BSs = new ByteArrayOutputStream();
1287 final ObjectOutputStream aCoos = new ObjectOutputStream(allC7BSs);
1288
1289 boolean didCompress = false; // True if any input was compressed.
1290 boolean didBetterWithDict = false; // True if any input was better compressed against any dictionary.
1291 for(final String t : testData)
1292 {
1293 final Compact7BitString c7bs = Compact7BitString.convertToCompact7BitString(t, null);
1294 final long nt1 = System.nanoTime();
1295 final String asString = c7bs.toString();
1296 final long nt2 = System.nanoTime();
1297 final long nt = nt2-nt1;
1298 // Estimate nanoseconds per char to decompress.
1299 final long nspc = nt / Math.max(1, asString.length());
1300 assertEquals("must be able to recover String value correctly",
1301 t, asString);
1302 assertEquals("must be able to recover String value after (de)serialisation",
1303 t, checkObjectCanBeSerialisedAndDeserialised(c7bs).toString());
1304
1305 // Internal format must be no more bytes than the String has chars,
1306 // ie half the memory space or less for most JVMs.
1307 final byte cd[] = c7bs.getInternalBytes();
1308 final int cl = ((null == cd) ? 0 : cd.length);
1309 Main.getOut().println("[chars => bytes (toString() ~"+nt+"ns, ~"+nspc+"ns/char): "+(t.length())+" => "+cl+": "+TextUtils.sanitiseForXML(t, 40, true)+"]");
1310 assertTrue("internal format must be no more bytes than the String has chars",
1311 cl <= t.length());
1312 // We expect at least one of the sample inputs to compress further...
1313 if(cl < t.length()) { didCompress = true; }
1314
1315 // Now compress sample input with static dictionaries we use.
1316 for(final Compact7BitString.StaticDictionary d : dicts)
1317 {
1318 final Compact7BitString c7bsA = Compact7BitString.convertToCompact7BitString(t, d);
1319 assertEquals("must be able to recover String value correctly: dictionary="+d,
1320 t, c7bsA.toString());
1321 assertEquals("must be able to recover String value after (de)serialisation: dictionary="+d,
1322 t, checkObjectCanBeSerialisedAndDeserialised(c7bsA).toString());
1323 final byte[] cdAlt = c7bsA.getInternalBytes();
1324 final int lenWithDict = (null == cdAlt) ? 0 : cdAlt.length;
1325 assertTrue("compression with static dictionary must be no worse than with no dictionary: dictionary="+d,
1326 lenWithDict <= cl);
1327 // Note if we do better with this compression dictionary.
1328 if(lenWithDict < cl)
1329 {
1330 didBetterWithDict = true;
1331 Main.getOut().println(" [with dictionary "+d.name+" => bytes: "+lenWithDict+".]");
1332 }
1333 }
1334
1335 aSoos.writeObject(t);
1336 aCoos.writeObject(c7bs);
1337 final Pair<CompressionLevel, byte[]> uS = GenUtils.compressObject(new Object[]{Compact7BitString.EMPTY, t}, CompressionLevel.NONE, false);
1338
1339 final Pair<CompressionLevel, byte[]> uC7BS = GenUtils.compressObject(new Object[]{Compact7BitString.EMPTY, c7bs}, CompressionLevel.NONE, false);
1340 // Main.getOut().println("[String/C7BS adjusted uncompressed serialised size: "+uS.second.length+"/"+uC7BS.second.length+".]");
1341 assertTrue("uncompressed form must be similar in size ("+uS.second.length+" vs "+uC7BS.second.length+") " + t,
1342 (4*uS.second.length)/3 + 32 >= uC7BS.second.length);
1343
1344 // Compute moderately-compressed serialised forms...
1345 final Pair<CompressionLevel, byte[]> cS = GenUtils.compressObject(new Object[]{Compact7BitString.EMPTY, t}, CompressionLevel.ZIP, false);
1346 final Pair<CompressionLevel, byte[]> cC7BS = GenUtils.compressObject(new Object[]{Compact7BitString.EMPTY, c7bs}, CompressionLevel.ZIP, false);
1347 // Main.getOut().println("[String/C7BS adjusted compressed serialised size: "+cS.second.length+"/"+cC7BS.second.length+".]");
1348 assertTrue("compressed form must be similar in size: S/C7BS="+cS.second.length+"/"+cC7BS.second.length,
1349 (4*cS.second.length)/3 + 32 >= cC7BS.second.length);
1350 }
1351 aSoos.close();
1352 aCoos.close();
1353
1354 assertTrue("at least one input should have compressed (fewer bytes than chars)", didCompress);
1355
1356 // Serialising all the test objects as C7BS should beat serialising them as plain Strings when compressed.
1357 final Pair<CompressionLevel, byte[]> aScomp = GenUtils.compressObject(allStrings.toByteArray(), GenUtils.MAX_SUPPORTED_COMPRESSION_LEVEL, false);
1358 final Pair<CompressionLevel, byte[]> aCcomp = GenUtils.compressObject(allC7BSs.toByteArray(), GenUtils.MAX_SUPPORTED_COMPRESSION_LEVEL, false);
1359 // Main.getOut().println("[Serialised String/C7BS compressed sequences: "+aScomp.second.length+"/"+aCcomp.second.length+".]");
1360 assertTrue("serialised sequence of (similar) C7BS instances should be not much larger than serialised plain Strings ("+aScomp.second.length+" vs "+aCcomp.second.length+")",
1361 (4*aScomp.second.length)/2 + 32 >=
1362 aCcomp.second.length);
1363
1364 assertTrue("at least one input should have compressed better with at least one of the static dictionaries!",
1365 didBetterWithDict);
1366 }
1367
1368 /**Test (de)serialisation of LocationMap.
1369 */
1370 public static void testLocationMap()
1371 throws Exception
1372 {
1373 checkEmptyInstancesAreZeroAndEqualAndSerializable(LocationMap.class);
1374 checkSerialisationPreservesEquality(new LocationMap());
1375
1376 final ExhibitPropsGlobalImmutable epgi = ExhibitPropsGlobalImmutable.loadFromDataDir(new File(LocalProps.getDataDir()));
1377 // final LocationMap lm = epgi.getLocationMap();
1378
1379 // Note size of simple ZIPped (quickly-compressed) serialised form
1380 // of realistic map (wrapped in EPGI).
1381 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
1382 final GZIPOutputStream gos = new GZIPOutputStream(baos);
1383 final ObjectOutputStream oos = new ObjectOutputStream(gos);
1384 oos.writeObject(epgi);
1385 oos.close();
1386 Main.getOut().println("Compressed serialised ExhibitPropsGlobalImmutable size (bytes) ~ " + baos.size());
1387 }
1388
1389 /**Test correctness of some of our (de)compressors often used with serialisation.
1390 * This ensures that they correctly handle some sample data streams
1391 * and that they work correctly in the face of a mid-stream flush().
1392 */
1393 public static void testCompressors()
1394 throws Exception
1395 {
1396 // We'll use a variety of existing and new test data.
1397 final List<byte[]> testData = new ArrayList<byte[]>(64);
1398
1399 // Some synthetic edge/hard cases.
1400 testData.add(new byte[0]);
1401 testData.add(new byte[101]);
1402 final byte randomRubbish[] = new byte[rnd.nextInt(1<<16)];
1403 rnd.nextBytes(randomRubbish);
1404 testData.add(randomRubbish);
1405
1406 // Test some of the frozen serialised data streams as realistic samples.
1407 testData.add(serData_AllExhibitPropertiesDelta_empty);
1408 testData.add(serData_C7BS_20060803);
1409 testData.add(serData_ROBA_compS1_20050522);
1410 testData.add(serData_evv1_20050131_1512);
1411
1412 // Compressors to test (represented by their "levels" here).
1413 final CompressionLevel compressors[] =
1414 { CompressionLevel.LZMA, CompressionLevel.BZIP2, CompressionLevel.ZIP };
1415
1416 // Try all the compressors in turn...
1417 for(final CompressionLevel comp : compressors)
1418 {
1419 // Try each data sample in turn...
1420 for(final byte[] data : testData)
1421 {
1422 // Compress once with no flush()es and once with at least one.
1423 for(final boolean doFlush : new boolean[]{ false, true } )
1424 {
1425 final ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
1426 final OutputStream cos1;
1427 switch(comp)
1428 {
1429 /** Note that we actually test our flushable GZIP here... */
1430 case ZIP: cos1 = new FlushableGZIPOutputStream(baos1); break;
1431 case BZIP2: cos1 = new CBZip2OutputStream(baos1); break;
1432 case LZMA: cos1 = new LzmaOutputStream(baos1); break;
1433 default: throw new Error("unexpected/unsupported compression type");
1434 }
1435 if(doFlush && rnd.nextBoolean()) { cos1.flush(); }
1436 if(!doFlush || (data.length < 2))
1437 { cos1.write(data.clone()); }
1438 else
1439 {
1440 // Break the write into two pieces with a flush() between.
1441 final int flushAfter = rnd.nextInt(data.length - 1);
1442 final byte d1[] = new byte[flushAfter];
1443 final byte d2[] = new byte[data.length - flushAfter];
1444 System.arraycopy(data, 0, d1, 0, flushAfter);
1445 System.arraycopy(data, flushAfter, d2, 0, d2.length);
1446 cos1.write(d1);
1447 cos1.flush();
1448 cos1.write(d2);
1449 }
1450 if(doFlush && rnd.nextBoolean()) { cos1.flush(); }
1451 cos1.close();
1452 final byte compressed[] = baos1.toByteArray();
1453 //Main.getOut().println("[Input length / method / doFlush / output length = "+data.length+" / "+comp+" / "+doFlush+" / "+compressed.length+".]");
1454
1455 // Check that decompressing reproduces the original input.
1456 final ByteArrayInputStream bais = new ByteArrayInputStream(compressed);
1457 final InputStream dcis;
1458 switch(comp)
1459 {
1460 case ZIP: dcis = new GZIPInputStream(bais); break;
1461 case BZIP2: dcis = new CBZip2InputStream(bais); break;
1462 case LZMA: dcis = new LzmaInputStream(bais); break;
1463 default: throw new Error("unexpected/unsupported compression type");
1464 }
1465 final DataInputStream dis = new DataInputStream(dcis);
1466 final byte decompData[] = new byte[data.length];
1467 dis.readFully(decompData);
1468 assertTrue("must be able to recover data after decompression", Arrays.equals(data, decompData));
1469 assertEquals("must be no trailing garbage in decompressed input", -1, dis.read());
1470 }
1471 }
1472 }
1473 }
1474
1475 /**Old-style String keys and values PropertiesDiff value. */
1476 private static final byte serData_pdS16_20090714[/*212*/] = {
1477 -84, -19, 0, 5, 115, 114, 0, 42, // ....sr.*
1478 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1479 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1480 114, 67, 111, 114, 101, 46, 112, 114, // rCore.pr
1481 111, 112, 115, 46, 80, 114, 111, 112, // ops.Prop
1482 101, 114, 116, 105, 101, 115, 68, 105, // ertiesDi
1483 102, 102, 59, -58, -64, -15, -69, -1, // ff;.....
1484 91, -18, 3, 0, 2, 73, 0, 9, // [....I..
1485 115, 105, 122, 101, 65, 102, 116, 101, // sizeAfte
1486 114, 73, 0, 10, 115, 105, 122, 101, // rI..size
1487 66, 101, 102, 111, 114, 101, 120, 112, // Beforexp
1488 0, 0, 0, 3, 0, 0, 0, 3, // ........
1489 116, 0, 7, 100, 101, 108, 101, 116, // t..delet
1490 101, 100, 117, 114, 0, 19, 91, 76, // edur..[L
1491 106, 97, 118, 97, 46, 108, 97, 110, // java.lan
1492 103, 46, 83, 116, 114, 105, 110, 103, // g.String
1493 59, -83, -46, 86, -25, -23, 29, 123, // ;..V...{
1494 71, 2, 0, 0, 120, 112, 0, 0, // G...xp..
1495 0, 2, 116, 0, 7, 99, 104, 97, // ..t..cha
1496 110, 103, 101, 100, 116, 0, 3, 110, // ngedt..n
1497 101, 119, 117, 113, 0, 126, 0, 3, // ewuq.~..
1498 0, 0, 0, 2, 116, 0, 26, 110, // ....t..n
1499 101, 119, 118, 97, 108, 117, 101, 44, // ewvalue,
1500 32, 121, 101, 97, 104, 32, 98, 97, // .yeah.ba
1501 98, 121, 44, 32, 121, 101, 97, 104, // by,.yeah
1502 33, 116, 0, 7, 119, 111, 116, 99, // !t..wotc
1503 104, 97, 33, 120, // ha!x
1504 };
1505
1506 /**New-style String keys and values PropertiesDiff value. */
1507 private static final byte serData_pdvar_20090714[/*342*/] = {
1508 -84, -19, 0, 5, 115, 114, 0, 42, // ....sr.*
1509 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1510 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1511 114, 67, 111, 114, 101, 46, 112, 114, // rCore.pr
1512 111, 112, 115, 46, 80, 114, 111, 112, // ops.Prop
1513 101, 114, 116, 105, 101, 115, 68, 105, // ertiesDi
1514 102, 102, 59, -58, -64, -15, -69, -1, // ff;.....
1515 91, -18, 3, 0, 2, 73, 0, 9, // [....I..
1516 115, 105, 122, 101, 65, 102, 116, 101, // sizeAfte
1517 114, 73, 0, 10, 115, 105, 122, 101, // rI..size
1518 66, 101, 102, 111, 114, 101, 120, 112, // Beforexp
1519 0, 0, 0, 3, 0, 0, 0, 3, // ........
1520 115, 114, 0, 26, 111, 114, 103, 46, // sr..org.
1521 104, 100, 46, 100, 46, 112, 103, 50, // hd.d.pg2
1522 107, 46, 115, 118, 114, 67, 111, 114, // k.svrCor
1523 101, 46, 78, 97, 109, 101, 38, -44, // e.Name&.
1524 -30, 17, 1, -80, 121, -16, 3, 0, // ....y...
1525 2, 83, 0, 14, 116, 101, 114, 109, // .S..term
1526 105, 110, 105, 76, 101, 110, 103, 116, // iniLengt
1527 104, 115, 76, 0, 4, 112, 114, 101, // hsL..pre
1528 118, 116, 0, 28, 76, 111, 114, 103, // vt..Lorg
1529 47, 104, 100, 47, 100, 47, 112, 103, // /hd/d/pg
1530 50, 107, 47, 115, 118, 114, 67, 111, // 2k/svrCo
1531 114, 101, 47, 78, 97, 109, 101, 59, // re/Name;
1532 120, 112, 0, 0, 112, 119, 8, 7, // xp..pw..
1533 100, 101, 108, 101, 116, 101, 100, 120, // deletedx
1534 117, 114, 0, 25, 91, 76, 106, 97, // ur..[Lja
1535 118, 97, 46, 108, 97, 110, 103, 46, // va.lang.
1536 67, 104, 97, 114, 83, 101, 113, 117, // CharSequ
1537 101, 110, 99, 101, 59, 11, -36, -59, // ence;...
1538 -43, 2, -39, 89, -121, 2, 0, 0, // ...Y....
1539 120, 112, 0, 0, 0, 4, 116, 0, // xp....t.
1540 7, 99, 104, 97, 110, 103, 101, 100, // .changed
1541 116, 0, 3, 110, 101, 119, 115, 113, // t..newsq
1542 0, 126, 0, 2, 0, 3, 115, 113, // .~....sq
1543 0, 126, 0, 2, 0, 0, 112, 119, // .~....pw
1544 4, 3, 110, 101, 119, 120, 119, 24, // ..newxw.
1545 23, 118, 97, 108, 117, 101, 44, 32, // .value,.
1546 121, 101, 97, 104, 32, 98, 97, 98, // yeah.bab
1547 121, 44, 32, 121, 101, 97, 104, 33, // y,.yeah!
1548 120, 115, 113, 0, 126, 0, 2, 0, // xsq.~...
1549 0, 112, 119, 8, 7, 119, 111, 116, // .pw..wot
1550 99, 104, 97, 33, 120, 120, // cha!xx
1551 };
1552
1553 /**Test behaviour of PropertiesDiff object.
1554 * This is primarily used to allow efficient transmission of
1555 * properties values/updates over the wire.
1556 */
1557 public static void testPropertiesDiff()
1558 throws Exception
1559 {
1560 // Test that empty object is valid.
1561 final PropertiesDiff emptyDiff = PropertiesDiff.EMPTY_DIFF;
1562 (emptyDiff).validateObject();
1563
1564 // Test that an (empty) object can survive (de)serialisation intact.
1565 checkEmptyInstancesAreZeroAndEqualAndSerializable(PropertiesDiff.class);
1566
1567 // Check that the diff from one empty Properties set to another is empty.
1568 final Properties emptyProps = new Properties();
1569 assertEquals("Diff between empty Properties sets must be empty",
1570 emptyDiff, PropertiesDiff.createDiff(emptyProps, emptyProps, true, rnd.nextBoolean()));
1571 checkSerialisationPreservesEquality(emptyProps);
1572
1573 // Check that the diff between an empty property set and a non-empty one is NOT empty.
1574 final Properties oneEntry = new Properties();
1575 oneEntry.setProperty("hello", "world");
1576 assertFalse("Diff between empty Properties and non-empty sets must not be empty",
1577 emptyDiff.equals(PropertiesDiff.createDiff(oneEntry, emptyProps, true, rnd.nextBoolean())));
1578 assertFalse("Diff between empty Properties and non-empty sets must not be empty",
1579 emptyDiff.equals(PropertiesDiff.createDiff(emptyProps, oneEntry, true, rnd.nextBoolean())));
1580 checkSerialisationPreservesEquality(oneEntry);
1581
1582 // Construct simple properties set with one of each pertinent type:
1583 // * unchanged
1584 // * deleted
1585 // * new/added
1586 // * changed
1587 final Properties p1 = new Properties();
1588 p1.setProperty("deleted", "byebye, baby, byebye");
1589 p1.setProperty("changed", "oldvalue");
1590 final Properties p2 = new Properties();
1591 p1.setProperty("unchanged", "sameold");
1592 p2.setProperty("unchanged", "sameold");
1593 p2.setProperty("changed", "newvalue, yeah baby, yeah!");
1594 p2.setProperty("new", "wotcha!");
1595 final PropertiesDiff diff1 = PropertiesDiff.createDiff(p1, p2, true, rnd.nextBoolean());
1596 assertFalse("This diff should not be empty", diff1.isEmpty());
1597 assertFalse("This diff should not be empty (==emptyDiff)", emptyDiff.equals(diff1));
1598 assertNotSame("This diff's hash should not be zero", 0, diff1.hashCode());
1599 final Properties synthP2 = PropertiesDiff.applyDiff(p1, diff1);
1600 assertEquals("Must be able to reconstruct 'p2' Properties using diff",
1601 p2, synthP2);
1602 // Check that the diff survives (de)serialisation.
1603 checkSerialisationPreservesEquality(diff1);
1604 // Check that older/current frozen formats can be read...
1605 final PropertiesDiff frozen1a = (PropertiesDiff) (new ObjectInputStream(new ByteArrayInputStream(serData_pdS16_20090714))).readObject();
1606 assertEquals("Must be able to deserialise old-style pure-String (16-bit) instance", diff1, frozen1a);
1607 final PropertiesDiff frozen1b = (PropertiesDiff) (new ObjectInputStream(new ByteArrayInputStream(serData_pdvar_20090714))).readObject();
1608 assertEquals("Must be able to deserialise new-style compacted instance", diff1, frozen1b);
1609
1610 if(PRINT_SER_BYTES) { Main.getErr().println("WARNING: stopping tests early because of volume of output..."); return; } // TOO MUCH GARBAGE PRINTED OTHERWISE...
1611
1612
1613 // Create some random-ish Properties sets, diff them, etc.
1614 // Generally check for sensible behaviour.
1615 // We have one unique key in each the before and after set
1616 // to help check for correct behaviour.
1617 final String uniqueBeforeKey = "_unique_before_";
1618 final String uniqueAfterKey = "_unique_after_";
1619 // About half the time have a property and key each with 16-bit characters in them.
1620 final String key16Bit = "16bit\u3456";
1621 final String value16Bit = "abdc\u6723yasd the quick brown\nforx jumps!";
1622 try { Name.create(key16Bit); fail("key is pure 8-bit but shouldn't be"); }
1623 catch(final IllegalArgumentException e) { /* Correctly rejected non-8-bit value. */ }
1624 try { Name.create(value16Bit); fail("value is pure 8-bit but shouldn't be"); }
1625 catch(final IllegalArgumentException e) { /* Correctly rejected non-8-bit value. */ }
1626
1627 // Test (exponentially) increasing set sizes,
1628 // up to something reasonably representative.
1629 for(int i = 0; i <= 12; i += 2)
1630 {
1631 final Properties before = new Properties();
1632 before.setProperty(uniqueBeforeKey, "U" + rnd.nextLong());
1633 final Properties after = new Properties();
1634 after.setProperty(uniqueAfterKey, "U" + rnd.nextLong());
1635 for(int j = rnd.nextInt(1 + (1<<i)); --j >= 0; )
1636 {
1637 // We aim to have the "before" and "after" values very similar,
1638 // with a few deletions and additions/changes,
1639 // with the "after" value on average very slightly larger
1640 // to represent realistic slow growth/change of a Properties set.
1641 final String key = "key." + (rnd.nextLong() >>> 1);
1642 final String beforeValue = "val" + rnd.nextLong();
1643 // The "before" value is usually set.
1644 if(rnd.nextInt(17) != 0) { before.setProperty(key, beforeValue); }
1645 // The "after" value is usually the same as the before value.
1646 final String afterValue = (rnd.nextInt(101) != 0) ? beforeValue : "val" + rnd.nextLong();
1647 // The "after" value is usually set.
1648 if(rnd.nextInt(19) != 0) { after.setProperty(key, afterValue); }
1649 }
1650
1651 // About half the time have a property and key each with 16-bit characters in them.
1652 final boolean test16Bit = rnd.nextBoolean();
1653 if(test16Bit)
1654 { after.setProperty(key16Bit, value16Bit); }
1655
1656 // Simplest and most basic test.
1657 // Force reconstruction of "after" vaue from "before" + diff.
1658 final PropertiesDiff basicDiff = PropertiesDiff.createDiff(before, after, true, rnd.nextBoolean());
1659 assertFalse("This diff should not be empty", basicDiff.isEmpty());
1660 final Properties synthAfter = PropertiesDiff.applyDiff(before, basicDiff);
1661 assertEquals("Must be able to reconstruct 'after' Properties with correct size using diff",
1662 after.size(), synthAfter.size());
1663 assertEquals("Must be able to reconstruct 'after' Properties with correct key set using diff",
1664 after.keySet(), synthAfter.keySet());
1665 assertEquals("Must be able to reconstruct 'after' Properties with correct value set using diff",
1666 new HashSet<Object>(after.values()), new HashSet<Object>(synthAfter.values()));
1667 assertEquals("Must be able to reconstruct 'after' Properties using diff",
1668 after, synthAfter);
1669
1670 // Force reconstruction of "after" from empty.
1671 final PropertiesDiff eDiff = PropertiesDiff.createDiff(emptyProps, after, true, rnd.nextBoolean());
1672 final Properties synthAfterE = PropertiesDiff.applyDiff(emptyProps, eDiff);
1673 assertEquals("Must be able to reconstruct 'after' Properties from diff from empty Properties",
1674 after, synthAfterE);
1675
1676 // Force reverse diff of "before" from "after".
1677 final PropertiesDiff rDiff = PropertiesDiff.createDiff(after, before, true, rnd.nextBoolean());
1678 final Properties synthBefore = PropertiesDiff.applyDiff(after, rDiff);
1679 assertEquals("Must be able to reconstruct 'before' Properties from (reverse) diff from 'after'",
1680 before, synthBefore);
1681
1682 // Check that the diff survives (de)serialisation.
1683 final PropertiesDiff bdS = (PropertiesDiff)checkSerialisationPreservesEquality(basicDiff);
1684 checkSerialisationPreservesEquality(eDiff);
1685 checkSerialisationPreservesEquality(rDiff);
1686
1687 // Check that even 16-bit keys and values survive (de)serialisation.
1688 if(test16Bit)
1689 {
1690 final Properties sAdS = PropertiesDiff.applyDiff(before, bdS);
1691 assertEquals("16-bit keys and values must be viable", value16Bit, sAdS.get(key16Bit));
1692 }
1693 }
1694 }
1695
1696 /**Test behaviour of PropertiesBundleDiff object.
1697 * This is primarily used to allow efficient transmission of
1698 * properties values/updates over the wire.
1699 */
1700 public static void testPropertiesBundleDiff()
1701 throws Exception
1702 {
1703 // Test that empty object is valid.
1704 final PropertiesBundleDiff emptyBundleDiff = PropertiesBundleDiff.EMPTY_DIFF;
1705 (emptyBundleDiff).validateObject();
1706
1707 // Test that an (empty) object can survive (de)serialisation intact.
1708 checkEmptyInstancesAreZeroAndEqualAndSerializable(PropertiesBundleDiff.class);
1709
1710 // Hand-crafted small properties sets representative of small system.
1711 // Version 1.
1712 final Properties v1_treedec_properties = new Properties();
1713 final Properties v1_treedec_fr_properties = new Properties();
1714 final Properties v1_treedec_ERROR_properties = new Properties();
1715 final Map<String,Properties> v1 = new TreeMap<String,Properties>();
1716 v1.put("", v1_treedec_properties);
1717 v1_treedec_properties.setProperty("deleted", "byebye");
1718 v1_treedec_properties.setProperty("changed", "oldvalue");
1719 v1_treedec_properties.setProperty("unchanged", "sameold");
1720 v1.put("_fr", v1_treedec_fr_properties);
1721 v1_treedec_fr_properties.setProperty("world", "monde");
1722 v1.put("_ERROR", v1_treedec_ERROR_properties);
1723
1724 // Version 2, after update.
1725 final Map<String,Properties> v2 = new TreeMap<String,Properties>();
1726 final Properties v2_treedec_properties = new Properties();
1727 final Properties v2_treedec_fr_properties = new Properties();
1728 final Properties v2_treedec_zh_properties = new Properties();
1729 v2.put("", v2_treedec_properties);
1730 v2_treedec_properties.setProperty("unchanged", "sameold");
1731 v2_treedec_properties.setProperty("changed", "newvalue");
1732 v2_treedec_properties.setProperty("new", "wotcha!");
1733 v2.put("_fr", v2_treedec_fr_properties);
1734 v2_treedec_fr_properties.setProperty("world", "monde");
1735 v2.put("_zh", v2_treedec_zh_properties);
1736
1737 assertFalse("Modified properties set should not compare equal", v1.equals(v2));
1738
1739 final PropertiesBundleDiff pbDiff = PropertiesBundleDiff.createDiff(v1, v2, true, rnd.nextBoolean());
1740 assertFalse("Bundle diff should not be empty", pbDiff.isEmpty());
1741 assertNotSame("Bundle diff hash should not be zero", 0, pbDiff.hashCode());
1742 checkSerialisationPreservesEquality(pbDiff);
1743
1744 // Check that we can correctly recreate v2 from v1 + diff.
1745 final Map<String,Properties> v2Synth = PropertiesBundleDiff.applyDiff(v1, pbDiff);
1746 assertEquals("Reconstricted bundle should equal original", v2, v2Synth);
1747 }
1748
1749 /**Basic serialisation tests of EPGI and its diff.
1750 */
1751 public static void testEPGIBasic()
1752 throws Exception
1753 {
1754 // Check (de)serialisation of empty EPGI.
1755 checkObjectCanBeSerialisedAndDeserialised(new ExhibitPropsGlobalImmutable());
1756
1757 // Test that an (empty) EPGL object can survive (de)serialisation intact.
1758 checkEmptyInstancesAreZeroAndEqualAndSerializable(ExhibitPropsGlobalImmutable.class);
1759
1760 // Check (de)serialisation of realistic EPGI.
1761 final ExhibitPropsGlobalImmutable epgi = ExhibitPropsGlobalImmutable.loadFromDataDir(new File(LocalProps.getDataDir()));
1762 checkSerialisationPreservesEquality(epgi);
1763
1764 // Very basic EPGIDiff tests.
1765 checkObjectCanBeSerialisedAndDeserialised(new EPGIDiff());
1766 }
1767
1768
1769 /**Original String field format serialisation of ExhibitStaticAttr("a/a-A.a", 1, CoreConsts.GALLERY_EPOC_START). */
1770 private static final byte serData_esa1a_20090711[/*139*/] = {
1771 -84, -19, 0, 5, 115, 114, 0, 39, // ....sr.'
1772 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1773 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1774 114, 67, 111, 114, 101, 46, 69, 120, // rCore.Ex
1775 104, 105, 98, 105, 116, 83, 116, 97, // hibitSta
1776 116, 105, 99, 65, 116, 116, 114, -115, // ticAttr.
1777 -69, -98, -10, -103, 64, -92, 113, 2, // ....@.q.
1778 0, 3, 74, 0, 6, 108, 101, 110, // ..J..len
1779 103, 116, 104, 74, 0, 9, 116, 105, // gthJ..ti
1780 109, 101, 115, 116, 97, 109, 112, 76, // mestampL
1781 0, 8, 102, 105, 108, 101, 80, 97, // ..filePa
1782 116, 104, 116, 0, 18, 76, 106, 97, // tht..Lja
1783 118, 97, 47, 108, 97, 110, 103, 47, // va/lang/
1784 83, 116, 114, 105, 110, 103, 59, 120, // String;x
1785 112, 0, 0, 0, 0, 0, 0, 0, // p.......
1786 1, 0, 0, 0, -65, 6, -28, -96, // ........
1787 0, 116, 0, 7, 97, 47, 97, 45, // .t..a/a-
1788 65, 46, 97, // A.a
1789 };
1790
1791 /**CharSequence field format serialisation of ExhibitStaticAttr("a/a-A.a", 1, CoreConsts.GALLERY_EPOC_START) with run-time String field type. */
1792 private static final byte serData_esa1b_20090711[/*145*/] = {
1793 -84, -19, 0, 5, 115, 114, 0, 39, // ....sr.'
1794 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1795 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1796 114, 67, 111, 114, 101, 46, 69, 120, // rCore.Ex
1797 104, 105, 98, 105, 116, 83, 116, 97, // hibitSta
1798 116, 105, 99, 65, 116, 116, 114, -115, // ticAttr.
1799 -69, -98, -10, -103, 64, -92, 113, 2, // ....@.q.
1800 0, 3, 74, 0, 6, 108, 101, 110, // ..J..len
1801 103, 116, 104, 74, 0, 9, 116, 105, // gthJ..ti
1802 109, 101, 115, 116, 97, 109, 112, 76, // mestampL
1803 0, 8, 102, 105, 108, 101, 80, 97, // ..filePa
1804 116, 104, 116, 0, 24, 76, 106, 97, // tht..Lja
1805 118, 97, 47, 108, 97, 110, 103, 47, // va/lang/
1806 67, 104, 97, 114, 83, 101, 113, 117, // CharSequ
1807 101, 110, 99, 101, 59, 120, 112, 0, // ence;xp.
1808 0, 0, 0, 0, 0, 0, 1, 0, // ........
1809 0, 0, -65, 6, -28, -96, 0, 116, // .......t
1810 0, 7, 97, 47, 97, 45, 65, 46, // ..a/a-A.
1811 97, // a
1812 };
1813
1814 /**CharSequence field format serialisation of ExhibitStaticAttr("a/a-A.a", 1, CoreConsts.GALLERY_EPOC_START) with run-time Name.ExhibitFull field type. */
1815 private static final byte serData_esa1c_20090711[/*300*/] = {
1816 -84, -19, 0, 5, 115, 114, 0, 39, // ....sr.'
1817 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1818 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1819 114, 67, 111, 114, 101, 46, 69, 120, // rCore.Ex
1820 104, 105, 98, 105, 116, 83, 116, 97, // hibitSta
1821 116, 105, 99, 65, 116, 116, 114, -115, // ticAttr.
1822 -69, -98, -10, -103, 64, -92, 113, 2, // ....@.q.
1823 0, 3, 74, 0, 6, 108, 101, 110, // ..J..len
1824 103, 116, 104, 74, 0, 9, 116, 105, // gthJ..ti
1825 109, 101, 115, 116, 97, 109, 112, 76, // mestampL
1826 0, 8, 102, 105, 108, 101, 80, 97, // ..filePa
1827 116, 104, 116, 0, 24, 76, 106, 97, // tht..Lja
1828 118, 97, 47, 108, 97, 110, 103, 47, // va/lang/
1829 67, 104, 97, 114, 83, 101, 113, 117, // CharSequ
1830 101, 110, 99, 101, 59, 120, 112, 0, // ence;xp.
1831 0, 0, 0, 0, 0, 0, 1, 0, // ........
1832 0, 0, -65, 6, -28, -96, 0, 115, // .......s
1833 114, 0, 38, 111, 114, 103, 46, 104, // r.&org.h
1834 100, 46, 100, 46, 112, 103, 50, 107, // d.d.pg2k
1835 46, 115, 118, 114, 67, 111, 114, 101, // .svrCore
1836 46, 78, 97, 109, 101, 36, 69, 120, // .Name$Ex
1837 104, 105, 98, 105, 116, 70, 117, 108, // hibitFul
1838 108, 124, -48, -33, -108, 52, -93, -31, // l|...4..
1839 9, 2, 0, 0, 120, 114, 0, 26, // ....xr..
1840 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1841 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1842 114, 67, 111, 114, 101, 46, 78, 97, // rCore.Na
1843 109, 101, 38, -44, -30, 17, 1, -80, // me&.....
1844 121, -16, 3, 0, 2, 83, 0, 14, // y....S..
1845 116, 101, 114, 109, 105, 110, 105, 76, // terminiL
1846 101, 110, 103, 116, 104, 115, 76, 0, // engthsL.
1847 4, 112, 114, 101, 118, 116, 0, 28, // .prevt..
1848 76, 111, 114, 103, 47, 104, 100, 47, // Lorg/hd/
1849 100, 47, 112, 103, 50, 107, 47, 115, // d/pg2k/s
1850 118, 114, 67, 111, 114, 101, 47, 78, // vrCore/N
1851 97, 109, 101, 59, 120, 112, 0, 0, // ame;xp..
1852 112, 119, 8, 7, 97, 47, 97, 45, // pw..a/a-
1853 65, 46, 97, 120, // A.ax
1854 };
1855
1856 /**Basic serialisation tests of ExhibitStaticAttrs. */
1857 public static void testESABasic()
1858 throws Exception
1859 {
1860 //assertImmutable(ExhibitStaticAttr.class);
1861 MutabilityAssert.assertInstancesOf(ExhibitStaticAttr.class, MutabilityMatchers.areImmutable(), AllowedReason.provided(Name.ExhibitFull.class).isAlsoImmutable());
1862
1863 final ExhibitStaticAttr esa1 = new ExhibitStaticAttr("a/a-A.a", 1, CoreConsts.GALLERY_EPOC_START);
1864 checkSerialisationPreservesEquality(esa1);
1865 // Check that we are able to extract this value from cold-store in old String field format.
1866 final ExhibitStaticAttr frozen1a = (ExhibitStaticAttr) (new ObjectInputStream(new ByteArrayInputStream(serData_esa1a_20090711))).readObject();
1867 assertEquals(esa1, frozen1a);
1868 // Check that we are able to extract this value from cold-store in CharSequence field format (with String run-time field type).
1869 final ExhibitStaticAttr frozen1b = (ExhibitStaticAttr) (new ObjectInputStream(new ByteArrayInputStream(serData_esa1b_20090711))).readObject();
1870 assertEquals(esa1, frozen1b);
1871 // Check that we are able to extract this value from cold-store in CharSequence field format (with Name.ExhibitFull run-time field type).
1872 final ExhibitStaticAttr frozen1c = (ExhibitStaticAttr) (new ObjectInputStream(new ByteArrayInputStream(serData_esa1c_20090711))).readObject();
1873 assertEquals(esa1, frozen1c);
1874 }
1875
1876 /**Serialisation of ExhibitFullName("a/a-A.a"). */
1877 private static final byte serData_efn1_20090711[/*169*/] = {
1878 -84, -19, 0, 5, 115, 114, 0, 38, // ....sr.&
1879 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1880 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1881 114, 67, 111, 114, 101, 46, 78, 97, // rCore.Na
1882 109, 101, 36, 69, 120, 104, 105, 98, // me$Exhib
1883 105, 116, 70, 117, 108, 108, 124, -48, // itFull|.
1884 -33, -108, 52, -93, -31, 9, 2, 0, // ..4.....
1885 0, 120, 114, 0, 26, 111, 114, 103, // .xr..org
1886 46, 104, 100, 46, 100, 46, 112, 103, // .hd.d.pg
1887 50, 107, 46, 115, 118, 114, 67, 111, // 2k.svrCo
1888 114, 101, 46, 78, 97, 109, 101, 38, // re.Name&
1889 -44, -30, 17, 1, -80, 121, -16, 3, // .....y..
1890 0, 2, 83, 0, 14, 116, 101, 114, // ..S..ter
1891 109, 105, 110, 105, 76, 101, 110, 103, // miniLeng
1892 116, 104, 115, 76, 0, 4, 112, 114, // thsL..pr
1893 101, 118, 116, 0, 28, 76, 111, 114, // evt..Lor
1894 103, 47, 104, 100, 47, 100, 47, 112, // g/hd/d/p
1895 103, 50, 107, 47, 115, 118, 114, 67, // g2k/svrC
1896 111, 114, 101, 47, 78, 97, 109, 101, // ore/Name
1897 59, 120, 112, 0, 0, 112, 119, 8, // ;xp..pw.
1898 7, 97, 47, 97, 45, 65, 46, 97, // .a/a-A.a
1899 120, // x
1900 };
1901
1902 /**Frozen pair of long values with common prefix and suffix. */
1903 private static final byte serData_efnpair_20090711[/*538*/] = {
1904 -84, -19, 0, 5, 115, 114, 0, 32, // ....sr..
1905 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
1906 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
1907 114, 67, 111, 114, 101, 46, 84, 117, // rCore.Tu
1908 112, 108, 101, 36, 80, 97, 105, 114, // ple$Pair
1909 -78, -8, -87, -112, 47, 28, 102, 56, // ..../.f8
1910 2, 0, 2, 76, 0, 5, 102, 105, // ...L..fi
1911 114, 115, 116, 116, 0, 18, 76, 106, // rstt..Lj
1912 97, 118, 97, 47, 108, 97, 110, 103, // ava/lang
1913 47, 79, 98, 106, 101, 99, 116, 59, // /Object;
1914 76, 0, 6, 115, 101, 99, 111, 110, // L..secon
1915 100, 113, 0, 126, 0, 1, 120, 112, // dq.~..xp
1916 115, 114, 0, 38, 111, 114, 103, 46, // sr.&org.
1917 104, 100, 46, 100, 46, 112, 103, 50, // hd.d.pg2
1918 107, 46, 115, 118, 114, 67, 111, 114, // k.svrCor
1919 101, 46, 78, 97, 109, 101, 36, 69, // e.Name$E
1920 120, 104, 105, 98, 105, 116, 70, 117, // xhibitFu
1921 108, 108, 124, -48, -33, -108, 52, -93, // ll|...4.
1922 -31, 9, 2, 0, 0, 120, 114, 0, // .....xr.
1923 26, 111, 114, 103, 46, 104, 100, 46, // .org.hd.
1924 100, 46, 112, 103, 50, 107, 46, 115, // d.pg2k.s
1925 118, 114, 67, 111, 114, 101, 46, 78, // vrCore.N
1926 97, 109, 101, 38, -44, -30, 17, 1, // ame&....
1927 -80, 121, -16, 3, 0, 2, 83, 0, // .y....S.
1928 14, 116, 101, 114, 109, 105, 110, 105, // .termini
1929 76, 101, 110, 103, 116, 104, 115, 76, // LengthsL
1930 0, 4, 112, 114, 101, 118, 116, 0, // ..prevt.
1931 28, 76, 111, 114, 103, 47, 104, 100, // .Lorg/hd
1932 47, 100, 47, 112, 103, 50, 107, 47, // /d/pg2k/
1933 115, 118, 114, 67, 111, 114, 101, 47, // svrCore/
1934 78, 97, 109, 101, 59, 120, 112, 0, // Name;xp.
1935 0, 112, 122, 0, 0, 1, 8, -1, // .pz.....
1936 -1, -2, -4, 116, 114, 97, 118, 101, // ...trave
1937 108, 47, 95, 109, 111, 114, 101, 50, // l/_more2
1938 48, 48, 54, 47, 95, 109, 111, 114, // 006/_mor
1939 101, 49, 48, 47, 116, 114, 101, 107, // e10/trek
1940 45, 52, 45, 100, 97, 121, 115, 45, // -4-days-
1941 102, 114, 111, 109, 45, 71, 97, 110, // from-Gan
1942 100, 101, 110, 45, 109, 111, 110, 97, // den-mona
1943 115, 116, 101, 114, 121, 45, 52, 53, // stery-45
1944 48, 48, 109, 45, 118, 105, 97, 45, // 00m-via-
1945 83, 104, 117, 103, 97, 45, 76, 97, // Shuga-La
1946 45, 112, 97, 115, 115, 45, 67, 104, // -pass-Ch
1947 105, 116, 117, 45, 76, 97, 45, 112, // itu-La-p
1948 97, 115, 115, 45, 53, 49, 48, 48, // ass-5100
1949 109, 45, 116, 114, 101, 107, 107, 101, // m-trekke
1950 114, 115, 45, 108, 111, 99, 97, 108, // rs-local
1951 45, 103, 117, 105, 100, 101, 115, 45, // -guides-
1952 103, 114, 97, 115, 115, 121, 45, 115, // grassy-s
1953 108, 111, 112, 101, 115, 45, 121, 97, // lopes-ya
1954 107, 115, 45, 115, 110, 111, 119, 45, // ks-snow-
1955 98, 108, 105, 122, 122, 97, 114, 100, // blizzard
1956 115, 45, 108, 97, 107, 101, 115, 45, // s-lakes-
1957 115, 97, 110, 100, 45, 100, 117, 110, // sand-dun
1958 101, 115, 45, 109, 97, 103, 105, 99, // es-magic
1959 97, 108, 45, 115, 99, 101, 110, 101, // al-scene
1960 114, 121, 45, 114, 111, 111, 102, 45, // ry-roof-
1961 111, 102, 45, 116, 104, 101, 45, 119, // of-the-w
1962 111, 114, 108, 100, 45, 118, 105, 101, // orld-vie
1963 119, 115, 45, 98, 105, 122, 97, 114, // ws-bizar
1964 114, 101, 45, 116, 114, 97, 110, 115, // re-trans
1965 112, 111, 114, 116, 45, 110, 101, 97, // port-nea
1966 114, 45, 76, 104, 97, 115, 97, 45, // r-Lhasa-
1967 84, 105, 98, 101, 116, 45, 56, 45, // Tibet-8-
1968 67, 75, 66, 46, 106, 112, 103, 120, // CKB.jpgx
1969 115, 113, 0, 126, 0, 3, 16, -5, // sq.~....
1970 113, 0, 126, 0, 6, 119, 2, 1, // q.~..w..
1971 57, 120, // 9x
1972 };
1973
1974 /**Basic serialisation tests of ExhibitFullName.
1975 */
1976 public static void testExhibitFullNameBasic()
1977 throws Exception
1978 {
1979 //assertImmutable(Name.ExhibitFull.class);
1980
1981 // Test a short value (suitable for shorter on-the-wire encoding).
1982 final Name.ExhibitFull efn1 = Name.ExhibitFull.create("a/a-A.a");
1983 checkSerialisationPreservesEquality(efn1);
1984 // Check that we are able to extract this value from cold-store.
1985 final Name.ExhibitFull frozen1 = (Name.ExhibitFull) (new ObjectInputStream(new ByteArrayInputStream(serData_efn1_20090711))).readObject();
1986 assertEquals(efn1, frozen1);
1987
1988 final String ln1 = "travel/_more2006/_more10/trek-4-days-from-Ganden-monastery-4500m-via-Shuga-La-pass-Chitu-La-pass-5100m-trekkers-local-guides-grassy-slopes-yaks-snow-blizzards-lakes-sand-dunes-magical-scenery-roof-of-the-world-views-bizarre-transport-near-Lhasa-Tibet-8-CKB.jpg";
1989 final String ln2 = "travel/_more2006/_more10/trek-4-days-from-Ganden-monastery-4500m-via-Shuga-La-pass-Chitu-La-pass-5100m-trekkers-local-guides-grassy-slopes-yaks-snow-blizzards-lakes-sand-dunes-magical-scenery-roof-of-the-world-views-bizarre-transport-near-Lhasa-Tibet-9-CKB.jpg";
1990
1991 // Test a long value (suitable for shorter on-the-wire encoding).
1992 final Name.ExhibitFull efn2a = Name.ExhibitFull.create(ln1);
1993 checkSerialisationPreservesEquality(efn2a);
1994
1995 // Test a couple pf values sharing a common prefix.
1996 final Name.ExhibitFull efn2b = Name.ExhibitFull.create(ln2, efn2a);
1997 final Tuple.Pair<Name.ExhibitFull,Name.ExhibitFull> pair = new Tuple.Pair<Name.ExhibitFull,Name.ExhibitFull>(efn2a, efn2b);
1998 checkSerialisationPreservesEquality(pair);
1999 // Check that we are able to extract this value from cold-store.
2000 final Tuple.Pair<Name.ExhibitFull,Name.ExhibitFull> frozenpair = (Tuple.Pair<Name.ExhibitFull,Name.ExhibitFull>) (new ObjectInputStream(new ByteArrayInputStream(serData_efnpair_20090711))).readObject();
2001 assertEquals(pair, frozenpair);
2002 }
2003
2004 /**Check that the current AEP serialisation is not less compact (when compressed) than the last one persisted.
2005 * This is a useful test for tuning the serialised format
2006 * against a realistic/large AEP instance.
2007 * <p>
2008 * This is quiet-ish unless the AEP changes in size significantly
2009 * when re-serialised and GZIPped. A big enough bloat is an error.
2010 * <p>
2011 * Often serialisation formats will compress slightly better
2012 * when less compact at the micro level
2013 * (ie if we avoid internally (over-)compressing each component first).
2014 * <p>
2015 * We only test with the last captured AEP, assumed to be the nearest to current.
2016 * <p>
2017 * This also measures code performance in the (de)serialisation path.
2018 */
2019 public static void testAEPSerialisationSize()
2020 throws Exception
2021 {
2022 final File lastAEPFile = new File(BackCompatTest.frozenAEPs.get(BackCompatTest.frozenAEPs.size()-1));
2023 final long lastAEPSize = lastAEPFile.length();
2024
2025 Main.getOut().println("Using captured AEP " + lastAEPFile);
2026 Main.getOut().println(" Size (compressed) on disc (bytes): " + lastAEPSize);
2027 final AllExhibitProperties lastAEP =
2028 (AllExhibitProperties) FileTools.deserialiseFromFile(lastAEPFile, true);
2029
2030 ////FIXME: TEMPORARY
2031 // // Extract and dump the largest current single XML and description values.
2032 // // We use these in other tests.
2033 // String biggestDesc = "";
2034 // String biggestMetaData = "";
2035 // for(final String e : lastAEP.aeid.getAllExhibitNamesSet())
2036 // {
2037 // final ExhibitPropsComputable epc = lastAEP.getExhibitPropsComputable(e);
2038 // if(epc != null)
2039 // {
2040 // final String md = epc.getMetadataAsXML();
2041 // if((null != md) && (md.length() > biggestMetaData.length()))
2042 // { biggestMetaData = md; }
2043 // }
2044 // final ExhibitPropsLoadable epl = lastAEP.getExhibitPropsLoadable(e);
2045 // if(epl != null)
2046 // {
2047 // final String desc = epl.getDescription();
2048 // if((null != desc) && (desc.length() > biggestDesc.length()))
2049 // { biggestDesc = desc; }
2050 // }
2051 // }
2052 // System.out.println("Biggest desc length="+biggestDesc.length());
2053 // System.out.println(biggestDesc);
2054 // System.out.println("Biggest metadata length="+biggestMetaData.length());
2055 // System.out.println(biggestMetaData);
2056
2057 final AllExhibitProperties newAEP = /* AllExhibitProperties.canonicalise */ (lastAEP);
2058 // Force to usual compact form in memory.
2059 final long cStart = System.nanoTime();
2060 newAEP.compact();
2061 final long cEnd = System.nanoTime();
2062 final long cNs = cEnd - cStart;
2063 Main.getOut().println("[AEP.compress() took "+cNs+"ns; mean ms/exhibit = " + (cNs / (newAEP.aeid.length * 1000))+"]");
2064 // Serialise to a byte[].
2065 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2066 // We may gzip the stream to remove some of the redundancy.
2067 final GZIPOutputStream gos = new GZIPOutputStream(baos);
2068 final DataOutputStream dos = new DataOutputStream(gos);
2069 final ObjectOutputStream oos = new ObjectOutputStream(dos);
2070 oos.writeObject(newAEP);
2071 oos.flush();
2072 gos.finish();
2073 oos.close();
2074 final byte data[] = baos.toByteArray();
2075
2076 Main.getOut().println(" SIZE NOW: " + data.length + ", uncompressed: " + dos.size());
2077 if(data.length > lastAEPSize)
2078 {
2079 Main.getErr().println(" FILE NOW LARGER, WHOOPS!");
2080 assertTrue("compressed serialised AEP size has grown significantly",
2081 data.length <= 8192 + ((11*lastAEPSize)/10));
2082 }
2083
2084 // Deserialise to try to verify that we really didn't break anything.
2085 final ByteArrayInputStream bais = new ByteArrayInputStream(data);
2086 final GZIPInputStream gis = new GZIPInputStream(bais);
2087 final ObjectInputStream ois = new ObjectInputStream(gis);
2088 final AllExhibitProperties newAEPSerDeser = (AllExhibitProperties) ois.readObject();
2089 assertEquals("AEP must survive (de)serialisation in new format", newAEP, newAEPSerDeser);
2090
2091 // Compute optimal static dictionaries for EPC and EPL (etc)
2092 // given the number of *unique* instances of each text held in memory,
2093 // ie assuming that we intern() duplicates
2094 // and so only save memory for the first instance compressed.
2095 if(ExhibitPropsComputable.sDict.isStatsEnabled() || ExhibitPropsLoadable.sDict.isStatsEnabled())
2096 {
2097 // Find all the unique EPL and EPC texts.
2098 final Set<String> uniqueEPLTexts = new HashSet<String>(newAEP.aeid.length);
2099 final Set<String> uniqueEPCTexts = new HashSet<String>(newAEP.aeid.length);
2100
2101 for(final Name.ExhibitFull name : newAEP.aeid.getAllExhibitNamesSorted())
2102 {
2103 final ExhibitPropsComputable epc = newAEP.getExhibitPropsComputable(name);
2104 if(epc != null)
2105 {
2106 // Capture what the class is internally trying to store/compress.
2107 final String s = epc.getMetadataAsXMLTrimmed().toString();
2108 if(s != null) { uniqueEPCTexts.add(s); }
2109 }
2110 final ExhibitPropsLoadable epl = newAEP.getExhibitPropsLoadable(name);
2111 if(epl != null)
2112 {
2113 final String s = epl.getDescription();
2114 if(s != null) { uniqueEPLTexts.add(s); }
2115 }
2116 }
2117
2118 // Zero out any stats for the static (de)compression dictionaries.
2119 ExhibitPropsComputable.sDict.resetStats();
2120 ExhibitPropsLoadable.sDict.resetStats();
2121 WebUtils.sDictMD.resetStats();
2122
2123 // Collect/dump stats to help generate good static dictionaries, etc.
2124 // Note that with a non-empty dictionary in place this shows residual "noise".
2125 // Skip/ignore items that cannot be compressed with Compact7BitString.
2126
2127 for(final String s : uniqueEPCTexts)
2128 { try { Compact7BitString.convertToCompact7BitString(s, ExhibitPropsComputable.sDict); } catch(final IllegalArgumentException e) { } }
2129 ExhibitPropsComputable.sDict.dumpStats(System.out);
2130
2131 for(final String s : uniqueEPLTexts)
2132 { try { Compact7BitString.convertToCompact7BitString(s, ExhibitPropsLoadable.sDict); } catch(final IllegalArgumentException e) { } }
2133 ExhibitPropsLoadable.sDict.dumpStats(System.out);
2134
2135 for(final Name.ExhibitFull name : newAEP.aeid.getAllExhibitNamesSorted())
2136 { WebUtils.getCatPageExhibitMetaDataHTMLRaw(name, newAEP); }
2137 WebUtils.sDictMD.dumpStats(System.out);
2138 }
2139
2140 // See how well we can compress a serialised AEP.
2141 final Pair<CompressionLevel, byte[]> squished = GenUtils.compressObject(newAEP, GenUtils.MAX_SUPPORTED_COMPRESSION_LEVEL, false);
2142 Main.getOut().println("Maximal compression of AEP: scheme="+squished.first+", size="+squished.second.length);
2143
2144 // Profile total cost of maximal decompression and deserialisation.
2145 // This reflects the costs at the (more common) client end.
2146 final ConcurrentHashMap<StackTraceElement, AtomicInteger> perfCounts = new ConcurrentHashMap<StackTraceElement, AtomicInteger>(1001);
2147 final ConcurrentMap<StackTraceElement, ConcurrentMap<StackTraceElement, AtomicInteger>> parentPerfCounts = new ConcurrentHashMap<StackTraceElement, ConcurrentMap<StackTraceElement, AtomicInteger>>(1001);
2148 final Thread perfMonitorThread = GenUtils.startThreadPerfMonitor(
2149 Thread.currentThread(),
2150 perfCounts,
2151 parentPerfCounts,
2152 "org.hd.", // Capture our code. // "org." ... and Apache (BZIP2) code.
2153 // Monitor thread for at most 1000s.
2154 System.currentTimeMillis() + 1000000, 50); // Sample relatively slowly, esp for WinTel development machine.
2155 try
2156 {
2157 final ObjectInputStream ois2 = new ObjectInputStream(GenUtils.wrapForDecompression(new ByteArrayInputStream(squished.second), squished.first));
2158 assertEquals("AEP must survive (de)serialisation after maximal compression", newAEP, ois2.readObject());
2159 }
2160 finally
2161 {
2162 GenUtils.stopPerfMonitorandDumpSamples(
2163 perfMonitorThread,
2164 "AEP (de)serialisation profile",
2165 perfCounts,
2166 parentPerfCounts,
2167 20, GenUtils.systemOutLogger);
2168 }
2169 }
2170
2171 /**Test text for CS8 (de)serialisation. */
2172 private static final String THE_HILLS_ARE_ALIVE = "The hills are alive...";
2173
2174 /**Serialised CS8 form. */
2175 private static final byte serData_CS8Bit[/*75*/] = {
2176 -84, -19, 0, 5, 115, 114, 0, 28, // ....sr..
2177 111, 114, 103, 46, 104, 100, 46, 100, // org.hd.d
2178 46, 112, 103, 50, 107, 46, 115, 118, // .pg2k.sv
2179 114, 67, 111, 114, 101, 46, 67, 83, // rCore.CS
2180 56, 66, 105, 116, -7, 103, -38, -32, // 8Bit.g..
2181 46, -21, -50, 91, 3, 0, 0, 120, // ...[...x
2182 112, 119, 23, 22, 84, 104, 101, 32, // pw..The.
2183 104, 105, 108, 108, 115, 32, 97, 114, // hills.ar
2184 101, 32, 97, 108, 105, 118, 101, 46, // e.alive.
2185 46, 46, 120, // ..x
2186 };
2187
2188 /**Test behaviour of CS8Bit object.
2189 * This is primarily used to allow efficient transmission of
2190 * properties values/updates over the wire.
2191 */
2192 public static void testCS8Bit()
2193 throws Exception
2194 {
2195 //assertImmutable(CS8Bit.class);
2196
2197 // Test that an (empty) object can survive (de)serialisation intact.
2198 checkSerialisationPreservesEquality(CS8Bit.EMPTY);
2199 // Check for a non-empty instance.
2200 final CS8Bit v1 = new CS8Bit(THE_HILLS_ARE_ALIVE);
2201 checkSerialisationPreservesEquality(v1);
2202 // Check that we are able to extract this value from cold-store.
2203 final CS8Bit frozen1 = (CS8Bit) (new ObjectInputStream(new ByteArrayInputStream(serData_CS8Bit))).readObject();
2204 assertEquals(v1, frozen1);
2205 // Check (de)serialisation for long enough text to require alternate form...
2206 final CS8Bit long1 = new CS8Bit("A long text to force alternate form of length value. The quick brown fox jumps over the lazy dog. Rats live on no evil star. Madam, I'm Adam.");
2207 assert(long1.length() > 128);
2208 checkSerialisationPreservesEquality(long1);
2209 }
2210
2211 /**Test that MetaData lightweight copy serialises the same as the original.
2212 * This allows a quick copy to be taken
2213 * and then saving to continue in parallel with further updates.
2214 */
2215 public static void testMetaDataLWCopy()
2216 throws Exception
2217 {
2218 final MetaData e = MetaData.createMetaData();
2219 final byte[] eb = serialiseToByteArray(e);
2220 assertNotNull(eb);
2221 final byte[] ecb = serialiseToByteArray(e.createLightweightCopy());
2222 assertTrue("serialising lightweight copy (of empty instance) should be identical to serialising original", Arrays.equals(eb, ecb));
2223
2224 // Check that old serialised MetaData can be loaded
2225 // and that serialising/deserialising a lightweight copy gives the same result.
2226 final File oldMDser = new File(BackCompatTest.testDataDir, "20110926._metadata.data");
2227 final MetaData md = (MetaData) FileTools.deserialiseFromFile(oldMDser, true);
2228 final MetaData mdc = md.createLightweightCopy();
2229 assertEquals("set of names should be same", md.getKnownExhibits(), mdc.getKnownExhibits());
2230 final byte[] mdcb = serialiseToByteArray(mdc.createLightweightCopy());
2231 final MetaData mdcd = (MetaData) deserialiseFromByteArray(mdcb);
2232 assertEquals("set of names should be same", md.getKnownExhibits(), mdcd.getKnownExhibits());
2233 // TODO: test content beyond just set of names...
2234 }
2235
2236
2237 /**Private source of OK pseudo-random numbers. */
2238 private static final Random rnd = new Random();
2239 }