Browse Source

initial import with version 151-SNAPSHOT

Wilfried Klaas 5 years ago
parent
commit
9194092b0b
99 changed files with 22710 additions and 0 deletions
  1. 6 0
      .checkstyle
  2. 29 0
      .classpath
  3. 109 0
      .fbprefs
  4. 1101 0
      .fbwarnings
  5. 5 0
      .gitignore
  6. 23 0
      .project
  7. 5 0
      build.properties
  8. 110 0
      build.xml
  9. 16 0
      jar.jardesc
  10. 14 0
      notice.txt
  11. 142 0
      pom.xml
  12. 0 0
      project.index
  13. 1714 0
      src/main/java/com/csvreader/CsvReader.java
  14. 599 0
      src/main/java/com/csvreader/CsvWriter.java
  15. 116 0
      src/main/java/de/mcs/utils/ArrayUtils.java
  16. 48 0
      src/main/java/de/mcs/utils/Checksum16.java
  17. 73 0
      src/main/java/de/mcs/utils/ConversionException.java
  18. 624 0
      src/main/java/de/mcs/utils/Conversions.java
  19. 64 0
      src/main/java/de/mcs/utils/ExtendedBoolean.java
  20. 218 0
      src/main/java/de/mcs/utils/ExtendedProperties.java
  21. 701 0
      src/main/java/de/mcs/utils/FileTool.java
  22. 112 0
      src/main/java/de/mcs/utils/FileZipper.java
  23. 1381 0
      src/main/java/de/mcs/utils/Files.java
  24. 135 0
      src/main/java/de/mcs/utils/FolderZipper.java
  25. 96 0
      src/main/java/de/mcs/utils/GetEnviroment.java
  26. 205 0
      src/main/java/de/mcs/utils/HTMLEncoder.java
  27. 105 0
      src/main/java/de/mcs/utils/MD5Utils.java
  28. 65 0
      src/main/java/de/mcs/utils/Macro.java
  29. 51 0
      src/main/java/de/mcs/utils/MacroReplacer.java
  30. 120 0
      src/main/java/de/mcs/utils/Macros.java
  31. 22 0
      src/main/java/de/mcs/utils/Maputils.java
  32. 31 0
      src/main/java/de/mcs/utils/MimetypeFactory.java
  33. 83 0
      src/main/java/de/mcs/utils/NamedList.java
  34. 87 0
      src/main/java/de/mcs/utils/NumberHelper.java
  35. 82 0
      src/main/java/de/mcs/utils/OSInformations.java
  36. 201 0
      src/main/java/de/mcs/utils/PortTest.java
  37. 237 0
      src/main/java/de/mcs/utils/PropertiesHelper.java
  38. 107 0
      src/main/java/de/mcs/utils/SHA256Utils.java
  39. 303 0
      src/main/java/de/mcs/utils/SoftHashMap.java
  40. 137 0
      src/main/java/de/mcs/utils/StartProcess.java
  41. 174 0
      src/main/java/de/mcs/utils/StreamHelper.java
  42. 65 0
      src/main/java/de/mcs/utils/StreamReaderThread.java
  43. 947 0
      src/main/java/de/mcs/utils/StringFormat.java
  44. 712 0
      src/main/java/de/mcs/utils/StringUtils.java
  45. 25 0
      src/main/java/de/mcs/utils/SystemPathes.java
  46. 142 0
      src/main/java/de/mcs/utils/ThreadPool.java
  47. 165 0
      src/main/java/de/mcs/utils/TimeHelper.java
  48. 44 0
      src/main/java/de/mcs/utils/URLUtilities.java
  49. 358 0
      src/main/java/de/mcs/utils/Version.java
  50. 76 0
      src/main/java/de/mcs/utils/WorkingThread.java
  51. 591 0
      src/main/java/de/mcs/utils/XMLChar.java
  52. 174 0
      src/main/java/de/mcs/utils/XMLConverter.java
  53. 249 0
      src/main/java/de/mcs/utils/ZipExtracter.java
  54. 290 0
      src/main/java/de/mcs/utils/ZipUpdater.java
  55. 582 0
      src/main/java/de/mcs/utils/caches/BlobCache.java
  56. 78 0
      src/main/java/de/mcs/utils/caches/FolderBlocker.java
  57. 21 0
      src/main/java/de/mcs/utils/caches/KeyAlreadyExistsException.java
  58. 618 0
      src/main/java/de/mcs/utils/caches/ObjectCache.java
  59. 103 0
      src/main/java/de/mcs/utils/caches/TimedCache.java
  60. 49 0
      src/main/java/de/mcs/utils/caches/TimedCacheEntry.java
  61. 1377 0
      src/main/java/de/mcs/utils/codecs/Base64.java
  62. 342 0
      src/main/java/de/mcs/utils/codecs/Base64Decoder.java
  63. 319 0
      src/main/java/de/mcs/utils/codecs/Base64Encoder.java
  64. 29 0
      src/main/java/de/mcs/utils/event/AbstractEvent.java
  65. 143 0
      src/main/java/de/mcs/utils/event/CallbackList.java
  66. 32 0
      src/main/java/de/mcs/utils/event/Delegate.java
  67. 158 0
      src/main/java/de/mcs/utils/event/DelegateImpl.java
  68. 20 0
      src/main/java/de/mcs/utils/event/Event.java
  69. 189 0
      src/main/java/de/mcs/utils/event/EventFactory.java
  70. 20 0
      src/main/java/de/mcs/utils/event/EventListener.java
  71. 103 0
      src/main/java/de/mcs/utils/event/ManagedProperty.java
  72. 194 0
      src/main/java/de/mcs/utils/io/RandomAccessInputStream.java
  73. 84 0
      src/main/java/de/mcs/utils/io/RandomAccessOutputStream.java
  74. 72 0
      src/main/java/de/mcs/utils/jsap/CommandKey.java
  75. 55 0
      src/main/java/de/mcs/utils/jsap/FileOption.java
  76. 47 0
      src/main/java/de/mcs/utils/jsap/JSAPHelper.java
  77. 253 0
      src/main/java/de/mcs/utils/jsap/ProcessCommandline.java
  78. 51 0
      src/main/java/de/mcs/utils/jsap/StringOption.java
  79. 49 0
      src/main/java/de/mcs/utils/jsap/Switch.java
  80. 16 0
      src/main/java/de/mcs/utils/nio/WatchCallBack.java
  81. 267 0
      src/main/java/de/mcs/utils/nio/WatchDir.java
  82. 79 0
      src/main/java/de/mcs/utils/nio/WatchDirThread.java
  83. 258 0
      src/main/java/de/mcs/utils/statistics/Count.java
  84. 75 0
      src/main/java/de/mcs/utils/threads/ThreadUtilities.java
  85. 9 0
      src/main/resource/de/mcs/utils/codecs/package.html
  86. 8 0
      src/main/resource/de/mcs/utils/package.html
  87. 5 0
      src/main/resource/overview.html
  88. 2091 0
      src/test/java/AllTests.java
  89. 34 0
      src/test/java/de/mcs/utils/TestTimeHelper.java
  90. 212 0
      src/test/java/de/mcs/utils/TestVersion.java
  91. 72 0
      src/test/java/de/mcs/utils/TstNumberHelper.java
  92. 72 0
      src/test/java/de/mcs/utils/TstStringUtils.java
  93. 100 0
      src/test/java/de/mcs/utils/caches/TestLocalCache.java
  94. 77 0
      src/test/java/de/mcs/utils/caches/TestObjectCache.java
  95. 110 0
      src/test/java/de/mcs/utils/codecs/TestBase64.java
  96. 157 0
      src/test/java/de/mcs/utils/event/TestEventFactory.java
  97. 36 0
      src/test/java/de/mcs/utils/event/TestManagedProperty.java
  98. 689 0
      src/test/java/de/mcs/utils/tstConversions.java
  99. 66 0
      src/test/java/de/mcs/utils/tstWorkingThread.java

+ 6 - 0
.checkstyle

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fileset-config file-format-version="1.2.0" simple-config="true">
+    <fileset name="all" enabled="true" check-config-name="MCS" local="false">
+        <file-match-pattern match-pattern="." include-pattern="true"/>
+    </fileset>
+</fileset-config>

+ 29 - 0
.classpath

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" path="src/main/resource"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" path="src/test/resource"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

+ 109 - 0
.fbprefs

@@ -0,0 +1,109 @@
+#FindBugs User Preferences
+#Tue Oct 23 16:21:47 CEST 2007
+detectorBadAppletConstructor=BadAppletConstructor|false
+detectorBadResultSetAccess=BadResultSetAccess|true
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
+detectorCloneIdiom=CloneIdiom|true
+detectorComparatorIdiom=ComparatorIdiom|true
+detectorConfusedInheritance=ConfusedInheritance|true
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
+detectorDontUseEnum=DontUseEnum|true
+detectorDroppedException=DroppedException|true
+detectorDumbMethodInvocations=DumbMethodInvocations|true
+detectorDumbMethods=DumbMethods|true
+detectorDuplicateBranches=DuplicateBranches|true
+detectorEmptyZipFileEntry=EmptyZipFileEntry|true
+detectorFinalizerNullsFields=FinalizerNullsFields|true
+detectorFindBadCast2=FindBadCast2|true
+detectorFindBadForLoop=FindBadForLoop|true
+detectorFindCircularDependencies=FindCircularDependencies|false
+detectorFindDeadLocalStores=FindDeadLocalStores|true
+detectorFindDoubleCheck=FindDoubleCheck|true
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true
+detectorFindFloatEquality=FindFloatEquality|true
+detectorFindHEmismatch=FindHEmismatch|true
+detectorFindInconsistentSync2=FindInconsistentSync2|true
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
+detectorFindMaskedFields=FindMaskedFields|true
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
+detectorFindNakedNotify=FindNakedNotify|true
+detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true
+detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true
+detectorFindNonShortCircuit=FindNonShortCircuit|true
+detectorFindNullDeref=FindNullDeref|true
+detectorFindOpenStream=FindOpenStream|true
+detectorFindPuzzlers=FindPuzzlers|true
+detectorFindRefComparison=FindRefComparison|true
+detectorFindReturnRef=FindReturnRef|true
+detectorFindRunInvocations=FindRunInvocations|true
+detectorFindSelfComparison=FindSelfComparison|true
+detectorFindSelfComparison2=FindSelfComparison2|true
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
+detectorFindSpinLoop=FindSpinLoop|true
+detectorFindSqlInjection=FindSqlInjection|true
+detectorFindTwoLockWait=FindTwoLockWait|true
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
+detectorFindUnconditionalWait=FindUnconditionalWait|true
+detectorFindUninitializedGet=FindUninitializedGet|true
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
+detectorFindUnreleasedLock=FindUnreleasedLock|true
+detectorFindUnsyncGet=FindUnsyncGet|true
+detectorFindUselessControlFlow=FindUselessControlFlow|true
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true
+detectorIncompatMask=IncompatMask|true
+detectorInefficientMemberAccess=InefficientMemberAccess|false
+detectorInefficientToArray=InefficientToArray|true
+detectorInfiniteLoop=InfiniteLoop|false
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
+detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
+detectorInitializationChain=InitializationChain|true
+detectorInstantiateStaticClass=InstantiateStaticClass|true
+detectorInvalidJUnitTest=InvalidJUnitTest|true
+detectorIteratorIdioms=IteratorIdioms|true
+detectorLazyInit=LazyInit|true
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
+detectorMethodReturnCheck=MethodReturnCheck|true
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
+detectorMutableLock=MutableLock|true
+detectorMutableStaticFields=MutableStaticFields|true
+detectorNaming=Naming|true
+detectorNumberConstructor=NumberConstructor|true
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
+detectorPublicSemaphores=PublicSemaphores|false
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
+detectorRedundantInterfaces=RedundantInterfaces|true
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
+detectorSerializableIdiom=SerializableIdiom|true
+detectorStartInConstructor=StartInConstructor|true
+detectorStaticCalendarDetector=StaticCalendarDetector|true
+detectorStringConcatenation=StringConcatenation|true
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
+detectorSwitchFallthrough=SwitchFallthrough|true
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
+detectorTestASM=TestASM|false
+detectorURLProblems=URLProblems|true
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
+detectorUnnecessaryMath=UnnecessaryMath|true
+detectorUnreadFields=UnreadFields|true
+detectorUseObjectEquals=UseObjectEquals|false
+detectorUselessSubclassMethod=UselessSubclassMethod|false
+detectorVarArgsProblems=VarArgsProblems|true
+detectorVolatileUsage=VolatileUsage|true
+detectorWaitInLoop=WaitInLoop|true
+detectorWrongMapIterator=WrongMapIterator|true
+detectorXMLFactoryBypass=XMLFactoryBypass|true
+detector_threshold=2
+filter_settings=Medium|PERFORMANCE,CORRECTNESS,I18N,MT_CORRECTNESS,BAD_PRACTICE,MALICIOUS_CODE,STYLE|false
+filter_settings_neg=|

+ 1101 - 0
.fbwarnings

@@ -0,0 +1,1101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<BugCollection version="1.1.0" sequence="0" timestamp="-1" analysisTimestamp="1193065894037" release="">
+  <Project filename="&lt;&lt;unnamed project&gt;&gt;">
+    <AuxClasspathEntry>E:\DATEN\SOURCEN\java\MCSUtils\bin</AuxClasspathEntry>
+    <AuxClasspathEntry>D:\sprachen\java\ide\eclipse_3.2\plugins\org.junit_3.8.1\junit.jar</AuxClasspathEntry>
+    <AuxClasspathEntry>D:\sprachen\java\ide\eclipse_3.2\plugins\org.junit_3.8.1\junit-addons-1.4.jar</AuxClasspathEntry>
+  </Project>
+  <BugInstance type="DLS_DEAD_LOCAL_STORE" priority="3" abbrev="DLS" category="STYLE">
+    <Class classname="de.mcs.utils.ArrayUtils">
+      <SourceLine classname="de.mcs.utils.ArrayUtils" sourcefile="ArrayUtils.java" sourcepath="de/mcs/utils/ArrayUtils.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.ArrayUtils" name="stringToArray" signature="(Ljava/lang/String;)[Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.ArrayUtils" start="69" end="114" startBytecode="0" endBytecode="91" sourcefile="ArrayUtils.java" sourcepath="de/mcs/utils/ArrayUtils.java"/>
+    </Method>
+    <LocalVariable name="stop" register="5" pc="18" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.ArrayUtils" start="73" end="73" startBytecode="16" endBytecode="16" sourcefile="ArrayUtils.java" sourcepath="de/mcs/utils/ArrayUtils.java"/>
+  </BugInstance>
+  <BugInstance type="ITA_INEFFICIENT_TO_ARRAY" priority="3" abbrev="ITA" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.ArrayUtils">
+      <SourceLine classname="de.mcs.utils.ArrayUtils" sourcefile="ArrayUtils.java" sourcepath="de/mcs/utils/ArrayUtils.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.ArrayUtils" name="stringToArray" signature="(Ljava/lang/String;)[Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.ArrayUtils" start="69" end="114" startBytecode="0" endBytecode="215" sourcefile="ArrayUtils.java" sourcepath="de/mcs/utils/ArrayUtils.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.ArrayUtils" start="114" end="114" startBytecode="209" endBytecode="209" sourcefile="ArrayUtils.java" sourcepath="de/mcs/utils/ArrayUtils.java"/>
+  </BugInstance>
+  <BugInstance type="DLS_DEAD_LOCAL_STORE" priority="2" abbrev="DLS" category="STYLE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="byteToDouble" signature="([BI)D" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="333" end="335" startBytecode="0" endBytecode="11" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <LocalVariable name="dval" register="2" pc="4" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.Conversions" start="333" end="333" startBytecode="3" endBytecode="3" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+  </BugInstance>
+  <BugInstance type="DLS_DEAD_LOCAL_STORE" priority="2" abbrev="DLS" category="STYLE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="byteToFloat" signature="([BI)F" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="257" end="259" startBytecode="0" endBytecode="11" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <LocalVariable name="fval" register="2" pc="4" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.Conversions" start="257" end="257" startBytecode="3" endBytecode="3" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+  </BugInstance>
+  <BugInstance type="DLS_DEAD_LOCAL_STORE" priority="2" abbrev="DLS" category="STYLE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="byteToInt" signature="([BI)I" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="181" end="183" startBytecode="0" endBytecode="11" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <LocalVariable name="ival" register="2" pc="4" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.Conversions" start="181" end="181" startBytecode="3" endBytecode="3" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+  </BugInstance>
+  <BugInstance type="DLS_DEAD_LOCAL_STORE" priority="2" abbrev="DLS" category="STYLE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="byteToLong" signature="([BI)J" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="295" end="297" startBytecode="0" endBytecode="11" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <LocalVariable name="lval" register="2" pc="4" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.Conversions" start="295" end="295" startBytecode="3" endBytecode="3" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+  </BugInstance>
+  <BugInstance type="DLS_DEAD_LOCAL_STORE" priority="2" abbrev="DLS" category="STYLE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="byteToShort" signature="([BI)S" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="219" end="221" startBytecode="0" endBytecode="11" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <LocalVariable name="sval" register="2" pc="4" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.Conversions" start="219" end="219" startBytecode="3" endBytecode="3" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+  </BugInstance>
+  <BugInstance type="DM_FP_NUMBER_CTOR" priority="3" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="byteToNumber" signature="([BLjava/lang/Object;)Ljava/lang/Object;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="616" end="643" startBytecode="0" endBytecode="241" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Conversions" start="630" end="630" startBytecode="128" endBytecode="128" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    <String value="Float(float)"/>
+    <String value="Float.valueOf(float)"/>
+  </BugInstance>
+  <BugInstance type="DM_FP_NUMBER_CTOR" priority="3" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="byteToNumber" signature="([BLjava/lang/Object;)Ljava/lang/Object;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="616" end="643" startBytecode="0" endBytecode="241" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Conversions" start="636" end="636" startBytecode="194" endBytecode="194" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    <String value="Double(double)"/>
+    <String value="Double.valueOf(double)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="byteToNumber" signature="([BLjava/lang/Object;)Ljava/lang/Object;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="616" end="643" startBytecode="0" endBytecode="241" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Conversions" start="622" end="622" startBytecode="38" endBytecode="38" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    <String value="Integer(int)"/>
+    <String value="Integer.valueOf(int)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="byteToNumber" signature="([BLjava/lang/Object;)Ljava/lang/Object;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="616" end="643" startBytecode="0" endBytecode="241" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Conversions" start="624" end="624" startBytecode="62" endBytecode="62" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    <String value="Byte(byte)"/>
+    <String value="Byte.valueOf(byte)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="byteToNumber" signature="([BLjava/lang/Object;)Ljava/lang/Object;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="616" end="643" startBytecode="0" endBytecode="241" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Conversions" start="627" end="627" startBytecode="95" endBytecode="95" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    <String value="Short(short)"/>
+    <String value="Short.valueOf(short)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="byteToNumber" signature="([BLjava/lang/Object;)Ljava/lang/Object;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="616" end="643" startBytecode="0" endBytecode="241" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Conversions" start="633" end="633" startBytecode="161" endBytecode="161" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    <String value="Long(long)"/>
+    <String value="Long.valueOf(long)"/>
+  </BugInstance>
+  <BugInstance type="PZLA_PREFER_ZERO_LENGTH_ARRAYS" priority="3" abbrev="PZLA" category="STYLE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="doubleToByte" signature="(D)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="588" end="588" startBytecode="0" endBytecode="1" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Conversions" start="588" end="588" startBytecode="1" endBytecode="1" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+  </BugInstance>
+  <BugInstance type="PZLA_PREFER_ZERO_LENGTH_ARRAYS" priority="3" abbrev="PZLA" category="STYLE">
+    <Class classname="de.mcs.utils.Conversions">
+      <SourceLine classname="de.mcs.utils.Conversions" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Conversions" name="floatToByte" signature="(F)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Conversions" start="535" end="535" startBytecode="0" endBytecode="1" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Conversions" start="535" end="535" startBytecode="1" endBytecode="1" sourcefile="Conversions.java" sourcepath="de/mcs/utils/Conversions.java"/>
+  </BugInstance>
+  <BugInstance type="PZLA_PREFER_ZERO_LENGTH_ARRAYS" priority="3" abbrev="PZLA" category="STYLE">
+    <Class classname="de.mcs.utils.ExtendedProperties">
+      <SourceLine classname="de.mcs.utils.ExtendedProperties" sourcefile="ExtendedProperties.java" sourcepath="de/mcs/utils/ExtendedProperties.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.ExtendedProperties" name="getStrings" signature="(Ljava/lang/String;)[Ljava/lang/String;" isStatic="false">
+      <SourceLine classname="de.mcs.utils.ExtendedProperties" start="178" end="182" startBytecode="0" endBytecode="20" sourcefile="ExtendedProperties.java" sourcepath="de/mcs/utils/ExtendedProperties.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.ExtendedProperties" start="182" end="182" startBytecode="20" endBytecode="20" sourcefile="ExtendedProperties.java" sourcepath="de/mcs/utils/ExtendedProperties.java"/>
+  </BugInstance>
+  <BugInstance type="SE_NO_SERIALVERSIONID" priority="3" abbrev="SnVI" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.ExtendedProperties">
+      <SourceLine classname="de.mcs.utils.ExtendedProperties" sourcefile="ExtendedProperties.java" sourcepath="de/mcs/utils/ExtendedProperties.java"/>
+    </Class>
+  </BugInstance>
+  <BugInstance type="DM_EXIT" priority="3" abbrev="Dm" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.FileTool">
+      <SourceLine classname="de.mcs.utils.FileTool" sourcefile="FileTool.java" sourcepath="de/mcs/utils/FileTool.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.FileTool" name="readFileToArrayList" signature="(Ljava/lang/String;)Ljava/util/ArrayList;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.FileTool" start="565" end="594" startBytecode="0" endBytecode="206" sourcefile="FileTool.java" sourcepath="de/mcs/utils/FileTool.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.FileTool" start="574" end="574" startBytecode="71" endBytecode="71" sourcefile="FileTool.java" sourcepath="de/mcs/utils/FileTool.java"/>
+  </BugInstance>
+  <BugInstance type="DM_EXIT" priority="3" abbrev="Dm" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.FileTool">
+      <SourceLine classname="de.mcs.utils.FileTool" sourcefile="FileTool.java" sourcepath="de/mcs/utils/FileTool.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.FileTool" name="readFileToString" signature="(Ljava/lang/String;)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.FileTool" start="605" end="636" startBytecode="0" endBytecode="209" sourcefile="FileTool.java" sourcepath="de/mcs/utils/FileTool.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.FileTool" start="614" end="614" startBytecode="71" endBytecode="71" sourcefile="FileTool.java" sourcepath="de/mcs/utils/FileTool.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.FileTool">
+      <SourceLine classname="de.mcs.utils.FileTool" sourcefile="FileTool.java" sourcepath="de/mcs/utils/FileTool.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.FileTool" name="writeStringToFile" signature="(Ljava/lang/String;Ljava/lang/String;)V" isStatic="true">
+      <SourceLine classname="de.mcs.utils.FileTool" start="649" end="658" startBytecode="0" endBytecode="22" sourcefile="FileTool.java" sourcepath="de/mcs/utils/FileTool.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.FileTool" start="651" end="651" startBytecode="9" endBytecode="9" sourcefile="FileTool.java" sourcepath="de/mcs/utils/FileTool.java"/>
+  </BugInstance>
+  <BugInstance type="DM_EXIT" priority="3" abbrev="Dm" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.Files">
+      <SourceLine classname="de.mcs.utils.Files" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Files" name="readFileToArrayList" signature="(Ljava/lang/String;)Ljava/util/ArrayList;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Files" start="801" end="827" startBytecode="0" endBytecode="205" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Files" start="808" end="808" startBytecode="71" endBytecode="71" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+  </BugInstance>
+  <BugInstance type="DM_EXIT" priority="3" abbrev="Dm" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.Files">
+      <SourceLine classname="de.mcs.utils.Files" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Files" name="readFileToString" signature="(Ljava/lang/String;)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Files" start="838" end="865" startBytecode="0" endBytecode="203" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Files" start="845" end="845" startBytecode="71" endBytecode="71" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+  </BugInstance>
+  <BugInstance type="ITA_INEFFICIENT_TO_ARRAY" priority="3" abbrev="ITA" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.Files">
+      <SourceLine classname="de.mcs.utils.Files" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Files" name="getFiles" signature="(Ljava/io/File;Z)[Ljava/io/File;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Files" start="268" end="281" startBytecode="0" endBytecode="107" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Files" start="281" end="281" startBytecode="101" endBytecode="101" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.Files">
+      <SourceLine classname="de.mcs.utils.Files" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Files" name="computeMD5FromFile" signature="(Ljava/lang/String;)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Files" start="116" end="170" startBytecode="0" endBytecode="125" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Files" start="122" end="122" startBytecode="11" endBytecode="11" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.Files">
+      <SourceLine classname="de.mcs.utils.Files" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Files" name="writeStringToFile" signature="(Ljava/lang/String;Ljava/lang/String;)V" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Files" start="877" end="886" startBytecode="0" endBytecode="22" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Files" start="879" end="879" startBytecode="9" endBytecode="9" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+  </BugInstance>
+  <BugInstance type="RV_RETURN_VALUE_IGNORED" priority="3" abbrev="RV" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.Files">
+      <SourceLine classname="de.mcs.utils.Files" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Files" name="fileCopy" signature="(Ljava/io/File;Ljava/io/File;Z)V" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Files" start="191" end="239" startBytecode="0" endBytecode="213" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+    </Method>
+    <Method classname="java.io.File" name="createNewFile" signature="()Z" isStatic="false" role="METHOD_CALLED">
+      <SourceLine classname="java.io.File"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.Files" start="192" end="192" startBytecode="8" endBytecode="8" sourcefile="Files.java" sourcepath="de/mcs/utils/Files.java"/>
+  </BugInstance>
+  <BugInstance type="DM_EXIT" priority="3" abbrev="Dm" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.FolderZipper">
+      <SourceLine classname="de.mcs.utils.FolderZipper" sourcefile="FolderZipper.java" sourcepath="de/mcs/utils/FolderZipper.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.FolderZipper" name="zipFolder" signature="(Ljava/lang/String;Ljava/lang/String;)V" isStatic="true">
+      <SourceLine classname="de.mcs.utils.FolderZipper" start="49" end="66" startBytecode="0" endBytecode="61" sourcefile="FolderZipper.java" sourcepath="de/mcs/utils/FolderZipper.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.FolderZipper" start="56" end="56" startBytecode="33" endBytecode="33" sourcefile="FolderZipper.java" sourcepath="de/mcs/utils/FolderZipper.java"/>
+  </BugInstance>
+  <BugInstance type="REC_CATCH_EXCEPTION" priority="3" abbrev="REC" category="STYLE">
+    <Class classname="de.mcs.utils.FolderZipper">
+      <SourceLine classname="de.mcs.utils.FolderZipper" sourcefile="FolderZipper.java" sourcepath="de/mcs/utils/FolderZipper.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.FolderZipper" name="addToZip" signature="(Ljava/lang/String;Ljava/lang/String;Ljava/util/zip/ZipOutputStream;)V" isStatic="true">
+      <SourceLine classname="de.mcs.utils.FolderZipper" start="83" end="116" startBytecode="0" endBytecode="232" sourcefile="FolderZipper.java" sourcepath="de/mcs/utils/FolderZipper.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.FolderZipper" start="112" end="112" startBytecode="225" endBytecode="225" sourcefile="FolderZipper.java" sourcepath="de/mcs/utils/FolderZipper.java"/>
+  </BugInstance>
+  <BugInstance type="DM_CONVERT_CASE" priority="3" abbrev="Dm" category="I18N">
+    <Class classname="de.mcs.utils.GetEnviroment">
+      <SourceLine classname="de.mcs.utils.GetEnviroment" sourcefile="GetEnviroment.java" sourcepath="de/mcs/utils/GetEnviroment.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.GetEnviroment" name="&lt;clinit&gt;" signature="()V" isStatic="true">
+      <SourceLine classname="de.mcs.utils.GetEnviroment" start="41" end="30" startBytecode="0" endBytecode="167" sourcefile="GetEnviroment.java" sourcepath="de/mcs/utils/GetEnviroment.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.GetEnviroment" start="45" end="45" startBytecode="9" endBytecode="9" sourcefile="GetEnviroment.java" sourcepath="de/mcs/utils/GetEnviroment.java"/>
+  </BugInstance>
+  <BugInstance type="PZLA_PREFER_ZERO_LENGTH_ARRAYS" priority="3" abbrev="PZLA" category="STYLE">
+    <Class classname="de.mcs.utils.MD5Utils">
+      <SourceLine classname="de.mcs.utils.MD5Utils" sourcefile="MD5Utils.java" sourcepath="de/mcs/utils/MD5Utils.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.MD5Utils" name="md5Bytes" signature="([B)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.MD5Utils" start="48" end="56" startBytecode="0" endBytecode="24" sourcefile="MD5Utils.java" sourcepath="de/mcs/utils/MD5Utils.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.MD5Utils" start="56" end="56" startBytecode="24" endBytecode="24" sourcefile="MD5Utils.java" sourcepath="de/mcs/utils/MD5Utils.java"/>
+  </BugInstance>
+  <BugInstance type="SF_SWITCH_FALLTHROUGH" priority="2" abbrev="SF" category="STYLE">
+    <Class classname="de.mcs.utils.NumberHelper">
+      <SourceLine classname="de.mcs.utils.NumberHelper" sourcefile="NumberHelper.java" sourcepath="de/mcs/utils/NumberHelper.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.NumberHelper" name="parseTime" signature="(Ljava/lang/String;)J" isStatic="true">
+      <SourceLine classname="de.mcs.utils.NumberHelper" start="66" end="84" startBytecode="0" endBytecode="119" sourcefile="NumberHelper.java" sourcepath="de/mcs/utils/NumberHelper.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.NumberHelper" start="71" end="73" startBytecode="80" endBytecode="81" sourcefile="NumberHelper.java" sourcepath="de/mcs/utils/NumberHelper.java"/>
+    <SourceLine classname="de.mcs.utils.NumberHelper" start="73" end="75" startBytecode="85" endBytecode="86" sourcefile="NumberHelper.java" sourcepath="de/mcs/utils/NumberHelper.java"/>
+    <SourceLine classname="de.mcs.utils.NumberHelper" start="75" end="77" startBytecode="90" endBytecode="91" sourcefile="NumberHelper.java" sourcepath="de/mcs/utils/NumberHelper.java"/>
+  </BugInstance>
+  <BugInstance type="DM_CONVERT_CASE" priority="3" abbrev="Dm" category="I18N">
+    <Class classname="de.mcs.utils.OSInformations">
+      <SourceLine classname="de.mcs.utils.OSInformations" sourcefile="OSInformations.java" sourcepath="de/mcs/utils/OSInformations.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.OSInformations" name="&lt;clinit&gt;" signature="()V" isStatic="true">
+      <SourceLine classname="de.mcs.utils.OSInformations" start="33" end="25" startBytecode="0" endBytecode="53" sourcefile="OSInformations.java" sourcepath="de/mcs/utils/OSInformations.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.OSInformations" start="38" end="38" startBytecode="17" endBytecode="17" sourcefile="OSInformations.java" sourcepath="de/mcs/utils/OSInformations.java"/>
+  </BugInstance>
+  <BugInstance type="ITA_INEFFICIENT_TO_ARRAY" priority="3" abbrev="ITA" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.PropertiesHelper">
+      <SourceLine classname="de.mcs.utils.PropertiesHelper" sourcefile="PropertiesHelper.java" sourcepath="de/mcs/utils/PropertiesHelper.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.PropertiesHelper" name="getKeys" signature="(Ljava/lang/String;)[Ljava/lang/String;" isStatic="false">
+      <SourceLine classname="de.mcs.utils.PropertiesHelper" start="52" end="59" startBytecode="0" endBytecode="68" sourcefile="PropertiesHelper.java" sourcepath="de/mcs/utils/PropertiesHelper.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.PropertiesHelper" start="59" end="59" startBytecode="62" endBytecode="62" sourcefile="PropertiesHelper.java" sourcepath="de/mcs/utils/PropertiesHelper.java"/>
+  </BugInstance>
+  <BugInstance type="SE_COMPARATOR_SHOULD_BE_SERIALIZABLE" priority="3" abbrev="Se" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.PropertiesHelper$1">
+      <SourceLine classname="de.mcs.utils.PropertiesHelper$1" sourcefile="PropertiesHelper.java" sourcepath="de/mcs/utils/PropertiesHelper.java"/>
+    </Class>
+  </BugInstance>
+  <BugInstance type="SIC_INNER_SHOULD_BE_STATIC_ANON" priority="3" abbrev="SIC" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.PropertiesHelper$1">
+      <SourceLine classname="de.mcs.utils.PropertiesHelper$1" sourcefile="PropertiesHelper.java" sourcepath="de/mcs/utils/PropertiesHelper.java"/>
+    </Class>
+  </BugInstance>
+  <BugInstance type="DM_CONVERT_CASE" priority="3" abbrev="Dm" category="I18N">
+    <Class classname="de.mcs.utils.StringFormat">
+      <SourceLine classname="de.mcs.utils.StringFormat" sourcefile="StringFormat.java" sourcepath="de/mcs/utils/StringFormat.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.StringFormat" name="fromHexString" signature="(Ljava/lang/String;)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.StringFormat" start="108" end="129" startBytecode="0" endBytecode="152" sourcefile="StringFormat.java" sourcepath="de/mcs/utils/StringFormat.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.StringFormat" start="108" end="108" startBytecode="1" endBytecode="1" sourcefile="StringFormat.java" sourcepath="de/mcs/utils/StringFormat.java"/>
+  </BugInstance>
+  <BugInstance type="UPM_UNCALLED_PRIVATE_METHOD" priority="3" abbrev="UPM" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.StringFormat">
+      <SourceLine classname="de.mcs.utils.StringFormat" sourcefile="StringFormat.java" sourcepath="de/mcs/utils/StringFormat.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.StringFormat" name="toHex" signature="(I)C" isStatic="true">
+      <SourceLine classname="de.mcs.utils.StringFormat" start="867" end="867" startBytecode="0" endBytecode="8" sourcefile="StringFormat.java" sourcepath="de/mcs/utils/StringFormat.java"/>
+    </Method>
+  </BugInstance>
+  <BugInstance type="WMI_WRONG_MAP_ITERATOR" priority="2" abbrev="WMI" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.StringFormat">
+      <SourceLine classname="de.mcs.utils.StringFormat" sourcefile="StringFormat.java" sourcepath="de/mcs/utils/StringFormat.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.StringFormat" name="getPropertyString" signature="(Ljava/util/Map;)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.StringFormat" start="589" end="601" startBytecode="0" endBytecode="138" sourcefile="StringFormat.java" sourcepath="de/mcs/utils/StringFormat.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.StringFormat" start="592" end="592" startBytecode="52" endBytecode="52" sourcefile="StringFormat.java" sourcepath="de/mcs/utils/StringFormat.java"/>
+  </BugInstance>
+  <BugInstance type="ITA_INEFFICIENT_TO_ARRAY" priority="3" abbrev="ITA" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.StringUtils">
+      <SourceLine classname="de.mcs.utils.StringUtils" sourcefile="StringUtils.java" sourcepath="de/mcs/utils/StringUtils.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.StringUtils" name="csvStringToArray" signature="(Ljava/lang/String;CC)[Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.StringUtils" start="326" end="328" startBytecode="0" endBytecode="30" sourcefile="StringUtils.java" sourcepath="de/mcs/utils/StringUtils.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.StringUtils" start="328" end="328" startBytecode="24" endBytecode="24" sourcefile="StringUtils.java" sourcepath="de/mcs/utils/StringUtils.java"/>
+  </BugInstance>
+  <BugInstance type="PZLA_PREFER_ZERO_LENGTH_ARRAYS" priority="3" abbrev="PZLA" category="STYLE">
+    <Class classname="de.mcs.utils.StringUtils">
+      <SourceLine classname="de.mcs.utils.StringUtils" sourcefile="StringUtils.java" sourcepath="de/mcs/utils/StringUtils.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.StringUtils" name="md5StringBytes" signature="(Ljava/lang/String;)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.StringUtils" start="484" end="492" startBytecode="0" endBytecode="27" sourcefile="StringUtils.java" sourcepath="de/mcs/utils/StringUtils.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.StringUtils" start="492" end="492" startBytecode="27" endBytecode="27" sourcefile="StringUtils.java" sourcepath="de/mcs/utils/StringUtils.java"/>
+  </BugInstance>
+  <BugInstance type="EC_UNRELATED_TYPES" priority="1" abbrev="EC" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.Version">
+      <SourceLine classname="de.mcs.utils.Version" sourcefile="Version.java" sourcepath="de/mcs/utils/Version.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.Version" name="main" signature="([Ljava/lang/String;)V" isStatic="true">
+      <SourceLine classname="de.mcs.utils.Version" start="235" end="270" startBytecode="0" endBytecode="197" sourcefile="Version.java" sourcepath="de/mcs/utils/Version.java"/>
+    </Method>
+    <Type descriptor="Lde/mcs/utils/Version;"/>
+    <Type descriptor="Ljava/lang/String;"/>
+    <SourceLine classname="de.mcs.utils.Version" start="250" end="250" startBytecode="215" endBytecode="215" sourcefile="Version.java" sourcepath="de/mcs/utils/Version.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.XarExtracter">
+      <SourceLine classname="de.mcs.utils.XarExtracter" sourcefile="XarExtracter.java" sourcepath="de/mcs/utils/XarExtracter.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.XarExtracter" name="extract" signature="(Ljava/lang/String;Ljava/lang/String;)Ljava/io/File;" isStatic="false">
+      <SourceLine classname="de.mcs.utils.XarExtracter" start="140" end="176" startBytecode="0" endBytecode="115" sourcefile="XarExtracter.java" sourcepath="de/mcs/utils/XarExtracter.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.XarExtracter" start="164" end="164" startBytecode="140" endBytecode="140" sourcefile="XarExtracter.java" sourcepath="de/mcs/utils/XarExtracter.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.XarExtracter">
+      <SourceLine classname="de.mcs.utils.XarExtracter" sourcefile="XarExtracter.java" sourcepath="de/mcs/utils/XarExtracter.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.XarExtracter" name="extractAll" signature="(ZI)V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.XarExtracter" start="225" end="265" startBytecode="0" endBytecode="134" sourcefile="XarExtracter.java" sourcepath="de/mcs/utils/XarExtracter.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.XarExtracter" start="238" end="238" startBytecode="107" endBytecode="107" sourcefile="XarExtracter.java" sourcepath="de/mcs/utils/XarExtracter.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.XarUpdater">
+      <SourceLine classname="de.mcs.utils.XarUpdater" sourcefile="XarUpdater.java" sourcepath="de/mcs/utils/XarUpdater.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.XarUpdater" name="addFile" signature="(Ljava/util/zip/ZipOutputStream;Ljava/lang/String;Ljava/io/File;[B)V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.XarUpdater" start="159" end="200" startBytecode="0" endBytecode="117" sourcefile="XarUpdater.java" sourcepath="de/mcs/utils/XarUpdater.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.XarUpdater" start="189" end="189" startBytecode="201" endBytecode="201" sourcefile="XarUpdater.java" sourcepath="de/mcs/utils/XarUpdater.java"/>
+  </BugInstance>
+  <BugInstance type="DE_MIGHT_IGNORE" priority="3" abbrev="DE" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decode" signature="(Ljava/lang/String;)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="709" end="762" startBytecode="0" endBytecode="240" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <Class classname="java.lang.Exception" role="CLASS_EXCEPTION">
+      <SourceLine classname="java.lang.Exception"/>
+    </Class>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="747" end="747" startBytecode="154" endBytecode="154" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="DE_MIGHT_IGNORE" priority="3" abbrev="DE" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeBytes" signature="([BIII)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="473" end="548" startBytecode="0" endBytecode="392" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <Class classname="java.lang.Exception" role="CLASS_EXCEPTION">
+      <SourceLine classname="java.lang.Exception"/>
+    </Class>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="496" end="496" startBytecode="92" endBytecode="92" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="DE_MIGHT_IGNORE" priority="3" abbrev="DE" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeObject" signature="(Ljava/io/Serializable;I)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="331" end="380" startBytecode="0" endBytecode="246" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <Class classname="java.lang.Exception" role="CLASS_EXCEPTION">
+      <SourceLine classname="java.lang.Exception"/>
+    </Class>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="360" end="360" startBytecode="106" endBytecode="106" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="DLS_DEAD_LOCAL_STORE" priority="3" abbrev="DLS" category="STYLE">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decodeFromFile" signature="(Ljava/lang/String;)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="879" end="919" startBytecode="0" endBytecode="108" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="buffer" register="4" pc="22" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="884" end="884" startBytecode="20" endBytecode="20" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_ALWAYS_NULL" priority="1" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decodeFromFile" signature="(Ljava/lang/String;)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="879" end="919" startBytecode="0" endBytecode="108" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="bis" register="2" pc="71" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="914" end="914" startBytecode="72" endBytecode="72" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeObject" signature="(Ljava/io/Serializable;I)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="331" end="380" startBytecode="0" endBytecode="120" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="gzos" register="5" pc="191" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="363" end="363" startBytecode="193" endBytecode="193" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decode" signature="(Ljava/lang/String;)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="709" end="762" startBytecode="0" endBytecode="122" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="bais" register="3" pc="166" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="754" end="754" startBytecode="167" endBytecode="167" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decode" signature="(Ljava/lang/String;)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="709" end="762" startBytecode="0" endBytecode="122" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="baos" register="5" pc="146" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="746" end="746" startBytecode="148" endBytecode="148" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decode" signature="(Ljava/lang/String;)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="709" end="762" startBytecode="0" endBytecode="122" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="gzis" register="4" pc="156" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="750" end="750" startBytecode="158" endBytecode="158" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decodeFromFile" signature="(Ljava/lang/String;)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="879" end="919" startBytecode="0" endBytecode="108" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="bis" register="2" pc="189" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="914" end="914" startBytecode="190" endBytecode="190" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decodeToFile" signature="(Ljava/lang/String;Ljava/lang/String;)Z" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="851" end="867" startBytecode="0" endBytecode="42" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="bos" register="3" pc="45" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="862" end="862" startBytecode="46" endBytecode="46" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decodeToObject" signature="(Ljava/lang/String;)Ljava/lang/Object;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="776" end="804" startBytecode="0" endBytecode="64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="bais" register="2" pc="67" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="795" end="795" startBytecode="68" endBytecode="68" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decodeToObject" signature="(Ljava/lang/String;)Ljava/lang/Object;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="776" end="804" startBytecode="0" endBytecode="64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="ois" register="3" pc="76" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="799" end="799" startBytecode="77" endBytecode="77" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeBytes" signature="([BIII)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="473" end="548" startBytecode="0" endBytecode="201" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="b64os" register="8" pc="128" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="499" end="499" startBytecode="130" endBytecode="130" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeBytes" signature="([BIII)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="473" end="548" startBytecode="0" endBytecode="201" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="baos" register="6" pc="138" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="503" end="503" startBytecode="140" endBytecode="140" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeBytes" signature="([BIII)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="473" end="548" startBytecode="0" endBytecode="201" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="gzos" register="7" pc="118" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="495" end="495" startBytecode="120" endBytecode="120" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeFromFile" signature="(Ljava/lang/String;)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="931" end="962" startBytecode="0" endBytecode="83" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="bis" register="2" pc="131" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="957" end="957" startBytecode="132" endBytecode="132" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeObject" signature="(Ljava/io/Serializable;I)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="331" end="380" startBytecode="0" endBytecode="120" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="b64os" register="3" pc="160" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="367" end="367" startBytecode="161" endBytecode="161" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeObject" signature="(Ljava/io/Serializable;I)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="331" end="380" startBytecode="0" endBytecode="120" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="baos" register="2" pc="169" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="371" end="371" startBytecode="170" endBytecode="170" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeObject" signature="(Ljava/io/Serializable;I)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="331" end="380" startBytecode="0" endBytecode="120" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="gzos" register="5" pc="150" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="363" end="363" startBytecode="152" endBytecode="152" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeObject" signature="(Ljava/io/Serializable;I)Ljava/lang/String;" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="331" end="380" startBytecode="0" endBytecode="120" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="oos" register="4" pc="140" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="359" end="359" startBytecode="142" endBytecode="142" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="NP_NULL_ON_SOME_PATH_EXCEPTION" priority="2" abbrev="NP" category="CORRECTNESS">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="encodeToFile" signature="([BLjava/lang/String;)Z" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="819" end="836" startBytecode="0" endBytecode="40" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <LocalVariable name="bos" register="3" pc="40" role="LOCAL_VARIABLE_NAMED"/>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="831" end="831" startBytecode="41" endBytecode="41" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="PZLA_PREFER_ZERO_LENGTH_ARRAYS" priority="3" abbrev="PZLA" category="STYLE">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decode" signature="([BII)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="656" end="694" startBytecode="0" endBytecode="187" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="688" end="688" startBytecode="156" endBytecode="156" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="PZLA_PREFER_ZERO_LENGTH_ARRAYS" priority="3" abbrev="PZLA" category="STYLE">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decodeFromFile" signature="(Ljava/lang/String;)[B" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="879" end="919" startBytecode="0" endBytecode="223" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="893" end="893" startBytecode="81" endBytecode="81" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="REC_CATCH_EXCEPTION" priority="3" abbrev="REC" category="STYLE">
+    <Class classname="de.mcs.utils.codecs.Base64">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.Base64" name="decode4to3" signature="([BI[BI)I" isStatic="true">
+      <SourceLine classname="de.mcs.utils.codecs.Base64" start="582" end="635" startBytecode="0" endBytecode="393" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.Base64" start="626" end="626" startBytecode="230" endBytecode="230" sourcefile="Base64.java" sourcepath="de/mcs/utils/codecs/Base64.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.TestBase64">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.TestBase64" name="testBase64InputStream" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="29" end="68" startBytecode="0" endBytecode="123" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="38" end="38" startBytecode="54" endBytecode="54" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.TestBase64">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.TestBase64" name="testBase64InputStream" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="29" end="68" startBytecode="0" endBytecode="123" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="44" end="44" startBytecode="98" endBytecode="98" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.TestBase64">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.TestBase64" name="testBase64InputStream" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="29" end="68" startBytecode="0" endBytecode="123" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="52" end="52" startBytecode="149" endBytecode="149" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.TestBase64">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.TestBase64" name="testBase64InputStream" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="29" end="68" startBytecode="0" endBytecode="123" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="58" end="58" startBytecode="183" endBytecode="183" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.TestBase64">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.TestBase64" name="testBase64OutputStream" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="71" end="110" startBytecode="0" endBytecode="123" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="80" end="80" startBytecode="54" endBytecode="54" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.TestBase64">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.TestBase64" name="testBase64OutputStream" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="71" end="110" startBytecode="0" endBytecode="123" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="86" end="86" startBytecode="98" endBytecode="98" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.TestBase64">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.TestBase64" name="testBase64OutputStream" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="71" end="110" startBytecode="0" endBytecode="123" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="94" end="94" startBytecode="149" endBytecode="149" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+  </BugInstance>
+  <BugInstance type="OS_OPEN_STREAM_EXCEPTION_PATH" priority="3" abbrev="OS" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.TestBase64">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.TestBase64" name="testBase64OutputStream" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="71" end="110" startBytecode="0" endBytecode="123" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="100" end="100" startBytecode="183" endBytecode="183" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+  </BugInstance>
+  <BugInstance type="RR_NOT_CHECKED" priority="2" abbrev="RR" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.TestBase64">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.TestBase64" name="testBase64InputStream" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="29" end="68" startBytecode="0" endBytecode="260" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Method>
+    <Method classname="java.io.FileInputStream" name="read" signature="([B)I" isStatic="false" role="METHOD_CALLED">
+      <SourceLine classname="java.io.FileInputStream"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="59" end="59" startBytecode="198" endBytecode="198" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+  </BugInstance>
+  <BugInstance type="RR_NOT_CHECKED" priority="2" abbrev="RR" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.codecs.TestBase64">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.codecs.TestBase64" name="testBase64OutputStream" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="71" end="110" startBytecode="0" endBytecode="260" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+    </Method>
+    <Method classname="java.io.FileInputStream" name="read" signature="([B)I" isStatic="false" role="METHOD_CALLED">
+      <SourceLine classname="java.io.FileInputStream"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.codecs.TestBase64" start="101" end="101" startBytecode="198" endBytecode="198" sourcefile="TestBase64.java" sourcepath="de/mcs/utils/codecs/TestBase64.java"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testByteToByteByte1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="416" end="421" startBytecode="0" endBytecode="43" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="417" end="417" startBytecode="12" endBytecode="12" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Byte(byte)"/>
+    <String value="Byte.valueOf(byte)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testIntToByteInteger" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="460" end="487" startBytecode="0" endBytecode="212" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="460" end="460" startBytecode="5" endBytecode="5" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Integer(int)"/>
+    <String value="Integer.valueOf(int)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testIntToByteInteger" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="460" end="487" startBytecode="0" endBytecode="212" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="464" end="464" startBytecode="27" endBytecode="27" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Integer(int)"/>
+    <String value="Integer.valueOf(int)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testIntToByteInteger" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="460" end="487" startBytecode="0" endBytecode="212" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="468" end="468" startBytecode="66" endBytecode="66" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Integer(int)"/>
+    <String value="Integer.valueOf(int)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testIntToByteInteger" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="460" end="487" startBytecode="0" endBytecode="212" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="472" end="472" startBytecode="93" endBytecode="93" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Integer(int)"/>
+    <String value="Integer.valueOf(int)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testIntToByteInteger" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="460" end="487" startBytecode="0" endBytecode="212" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="476" end="476" startBytecode="121" endBytecode="121" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Integer(int)"/>
+    <String value="Integer.valueOf(int)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testIntToByteInteger" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="460" end="487" startBytecode="0" endBytecode="212" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="480" end="480" startBytecode="155" endBytecode="155" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Integer(int)"/>
+    <String value="Integer.valueOf(int)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testIntToByteInteger" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="460" end="487" startBytecode="0" endBytecode="212" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="484" end="484" startBytecode="180" endBytecode="180" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Integer(int)"/>
+    <String value="Integer.valueOf(int)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testLongToByteLong1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="631" end="667" startBytecode="0" endBytecode="364" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="631" end="631" startBytecode="5" endBytecode="5" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Long(long)"/>
+    <String value="Long.valueOf(long)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testLongToByteLong1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="631" end="667" startBytecode="0" endBytecode="364" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="635" end="635" startBytecode="29" endBytecode="29" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Long(long)"/>
+    <String value="Long.valueOf(long)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testLongToByteLong1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="631" end="667" startBytecode="0" endBytecode="364" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="639" end="639" startBytecode="88" endBytecode="88" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Long(long)"/>
+    <String value="Long.valueOf(long)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testLongToByteLong1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="631" end="667" startBytecode="0" endBytecode="364" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="643" end="643" startBytecode="117" endBytecode="117" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Long(long)"/>
+    <String value="Long.valueOf(long)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testLongToByteLong1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="631" end="667" startBytecode="0" endBytecode="364" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="647" end="647" startBytecode="146" endBytecode="146" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Long(long)"/>
+    <String value="Long.valueOf(long)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testLongToByteLong1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="631" end="667" startBytecode="0" endBytecode="364" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="651" end="651" startBytecode="199" endBytecode="199" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Long(long)"/>
+    <String value="Long.valueOf(long)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testLongToByteLong1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="631" end="667" startBytecode="0" endBytecode="364" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="655" end="655" startBytecode="228" endBytecode="228" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Long(long)"/>
+    <String value="Long.valueOf(long)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testLongToByteLong1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="631" end="667" startBytecode="0" endBytecode="364" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="659" end="659" startBytecode="284" endBytecode="284" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Long(long)"/>
+    <String value="Long.valueOf(long)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testLongToByteLong1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="631" end="667" startBytecode="0" endBytecode="364" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="663" end="663" startBytecode="313" endBytecode="313" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Long(long)"/>
+    <String value="Long.valueOf(long)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testShortToByteShort1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="534" end="569" startBytecode="0" endBytecode="247" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="534" end="534" startBytecode="5" endBytecode="5" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Short(short)"/>
+    <String value="Short.valueOf(short)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testShortToByteShort1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="534" end="569" startBytecode="0" endBytecode="247" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="538" end="538" startBytecode="28" endBytecode="28" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Short(short)"/>
+    <String value="Short.valueOf(short)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testShortToByteShort1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="534" end="569" startBytecode="0" endBytecode="247" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="542" end="542" startBytecode="60" endBytecode="60" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Short(short)"/>
+    <String value="Short.valueOf(short)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testShortToByteShort1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="534" end="569" startBytecode="0" endBytecode="247" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="546" end="546" startBytecode="88" endBytecode="88" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Short(short)"/>
+    <String value="Short.valueOf(short)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testShortToByteShort1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="534" end="569" startBytecode="0" endBytecode="247" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="550" end="550" startBytecode="115" endBytecode="115" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Short(short)"/>
+    <String value="Short.valueOf(short)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testShortToByteShort1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="534" end="569" startBytecode="0" endBytecode="247" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="554" end="554" startBytecode="142" endBytecode="142" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Short(short)"/>
+    <String value="Short.valueOf(short)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testShortToByteShort1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="534" end="569" startBytecode="0" endBytecode="247" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="558" end="558" startBytecode="169" endBytecode="169" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Short(short)"/>
+    <String value="Short.valueOf(short)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testShortToByteShort1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="534" end="569" startBytecode="0" endBytecode="247" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="562" end="562" startBytecode="198" endBytecode="198" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Short(short)"/>
+    <String value="Short.valueOf(short)"/>
+  </BugInstance>
+  <BugInstance type="DM_NUMBER_CTOR" priority="2" abbrev="Dm" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+    <Method classname="de.mcs.utils.tstConversions" name="testShortToByteShort1" signature="()V" isStatic="false">
+      <SourceLine classname="de.mcs.utils.tstConversions" start="534" end="569" startBytecode="0" endBytecode="247" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Method>
+    <SourceLine classname="de.mcs.utils.tstConversions" start="566" end="566" startBytecode="223" endBytecode="223" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    <String value="Short(short)"/>
+    <String value="Short.valueOf(short)"/>
+  </BugInstance>
+  <BugInstance type="NM_CLASS_NAMING_CONVENTION" priority="2" abbrev="Nm" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.tstConversions">
+      <SourceLine classname="de.mcs.utils.tstConversions" sourcefile="tstConversions.java" sourcepath="de/mcs/utils/tstConversions.java"/>
+    </Class>
+  </BugInstance>
+  <BugInstance type="NM_CLASS_NAMING_CONVENTION" priority="2" abbrev="Nm" category="BAD_PRACTICE">
+    <Class classname="de.mcs.utils.tstWorkingThread">
+      <SourceLine classname="de.mcs.utils.tstWorkingThread" sourcefile="tstWorkingThread.java" sourcepath="de/mcs/utils/tstWorkingThread.java"/>
+    </Class>
+  </BugInstance>
+  <BugInstance type="SIC_INNER_SHOULD_BE_STATIC" priority="2" abbrev="SIC" category="PERFORMANCE">
+    <Class classname="de.mcs.utils.tstWorkingThread$MyRunnable">
+      <SourceLine classname="de.mcs.utils.tstWorkingThread$MyRunnable" sourcefile="tstWorkingThread.java" sourcepath="de/mcs/utils/tstWorkingThread.java"/>
+    </Class>
+    <SourceLine classname="de.mcs.utils.tstWorkingThread$MyRunnable" start="25" end="25" startBytecode="0" endBytecode="0" sourcefile="E:\DATEN\SOURCEN\java\MCSUtils\testsrc\de\mcs\utils\tstWorkingThread.java" sourcepath="de/mcs/utils/E:\DATEN\SOURCEN\java\MCSUtils\testsrc\de\mcs\utils\tstWorkingThread.java"/>
+  </BugInstance>
+  <Errors></Errors>
+  <FindBugsSummary timestamp="Thu, 21 Sep 2006 11:31:11 +0200" total_classes="24" total_bugs="108" total_size="0" num_packages="2" cpu_seconds="210.99" clock_seconds="56191.40" peak_mbytes="247.47" gc_seconds="30.22" priority_3="47" priority_2="59" priority_1="2">
+    <PackageStats package="de.mcs.utils" total_bugs="73" total_types="22" total_size="0" priority_3="32" priority_2="40" priority_1="1">
+      <ClassStats class="de.mcs.utils.ArrayUtils" interface="false" size="0" bugs="2" priority_3="2"/>
+      <ClassStats class="de.mcs.utils.Conversions" interface="false" size="0" bugs="13" priority_3="4" priority_2="9"/>
+      <ClassStats class="de.mcs.utils.ExtendedProperties" interface="false" size="0" bugs="2" priority_3="2"/>
+      <ClassStats class="de.mcs.utils.FileTool" interface="false" size="0" bugs="3" priority_3="3"/>
+      <ClassStats class="de.mcs.utils.Files" interface="false" size="0" bugs="6" priority_3="6"/>
+      <ClassStats class="de.mcs.utils.FolderZipper" interface="false" size="0" bugs="2" priority_3="2"/>
+      <ClassStats class="de.mcs.utils.GetEnviroment" interface="false" size="0" bugs="1" priority_3="1"/>
+      <ClassStats class="de.mcs.utils.MD5Utils" interface="false" size="0" bugs="1" priority_3="1"/>
+      <ClassStats class="de.mcs.utils.NumberHelper" interface="false" size="0" bugs="1" priority_2="1"/>
+      <ClassStats class="de.mcs.utils.OSInformations" interface="false" size="0" bugs="1" priority_3="1"/>
+      <ClassStats class="de.mcs.utils.PropertiesHelper" interface="false" size="0" bugs="1" priority_3="1"/>
+      <ClassStats class="de.mcs.utils.PropertiesHelper$1" interface="false" size="0" bugs="2" priority_3="2"/>
+      <ClassStats class="de.mcs.utils.StringFormat" interface="false" size="0" bugs="3" priority_3="2" priority_2="1"/>
+      <ClassStats class="de.mcs.utils.StringUtils" interface="false" size="0" bugs="2" priority_3="2"/>
+      <ClassStats class="de.mcs.utils.ThreadPool" interface="false" size="0" bugs="0"/>
+      <ClassStats class="de.mcs.utils.Version" interface="false" size="0" bugs="1" priority_1="1"/>
+      <ClassStats class="de.mcs.utils.WorkingThread" interface="false" size="0" bugs="0"/>
+      <ClassStats class="de.mcs.utils.XarExtracter" interface="false" size="0" bugs="2" priority_3="2"/>
+      <ClassStats class="de.mcs.utils.XarUpdater" interface="false" size="0" bugs="1" priority_3="1"/>
+      <ClassStats class="de.mcs.utils.tstConversions" interface="false" size="0" bugs="27" priority_2="27"/>
+      <ClassStats class="de.mcs.utils.tstWorkingThread" interface="false" size="0" bugs="1" priority_2="1"/>
+      <ClassStats class="de.mcs.utils.tstWorkingThread$MyRunnable" interface="false" size="0" bugs="1" priority_2="1"/>
+    </PackageStats>
+    <PackageStats package="de.mcs.utils.codecs" total_bugs="35" total_types="2" total_size="0" priority_3="15" priority_2="19" priority_1="1">
+      <ClassStats class="de.mcs.utils.codecs.Base64" interface="false" size="0" bugs="25" priority_3="7" priority_2="17" priority_1="1"/>
+      <ClassStats class="de.mcs.utils.codecs.TestBase64" interface="false" size="0" bugs="10" priority_3="8" priority_2="2"/>
+    </PackageStats>
+  </FindBugsSummary>
+  <ClassFeatures></ClassFeatures>
+  <History></History>
+</BugCollection>

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+
+\.settings/
+
+dist/
+target/

+ 23 - 0
.project

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>MCSUtils</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

+ 5 - 0
build.properties

@@ -0,0 +1,5 @@
+#MCSUtils interactive build information file. This file is created automatically. DO NOT CHANGE !
+#Mon, 08 Sep 2014 22:40:39 +0200
+releasenum=0.12
+build.date=08.09.2014 22\:40\:39
+build.num=130

+ 110 - 0
build.xml

@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project default="jar src distribute" name="MCSUtils">
+	<property name="app.name" value="MCSUtils">
+	</property>
+	<property name="doc" value="${basedir}/docs">
+	</property>
+	<property name="src" value="${basedir}/src">
+	</property>
+	<property name="javadoc" value="${doc}/javadoc">
+	</property>
+	<property name="library" value="${basedir}\lib">
+	</property>
+	<property name="distribution" value="${basedir}\dist">
+	</property>
+
+	<property name="noticename" value="notice.txt">
+	</property>
+
+	<fileset id="libraries" defaultexcludes="yes" dir="${library}">
+		<include name="**/*.jar" />
+	</fileset>
+
+	<fileset id="javadocs" defaultexcludes="yes" dir="${doc}">
+		<include name="javadoc/**/*" />
+	</fileset>
+
+	<target name="prepare_properties" depends="clean" description="reads properties and sets default values">
+		<!-- update build information by incrementing build-number and actualizing date -->
+		<propertyfile file="${basedir}/build.properties" comment="MCSUtils interactive build information file. This file is created automatically. DO NOT CHANGE !">
+			<entry key="build.num" type="int" default="00" operation="+" pattern="00" />
+			<entry key="build.date" type="date" value="now" pattern="dd.MM.yyyy HH:mm:ss" />
+		</propertyfile>
+
+		<!-- read build.properties -->
+		<property file="${basedir}/build.properties" />
+		<property name="releasenumber" value="${releasenum}.${build.num}">
+		</property>
+		<property name="jarname" value="${app.name}-${releasenumber}.jar">
+		</property>
+		<property name="zipjarname" value="${jarname}.zip">
+		</property>
+		<property name="jarsrcname" value="${app.name}-${releasenumber}-src.jar">
+		</property>
+		<property name="zipjarsrcname" value="${jarsrcname}.zip">
+		</property>
+		<property name="zipdoc" value="${app.name}-${releasenumber}-docs.zip">
+		</property>
+		<property name="zipall" value="${app.name}-${releasenumber}-all.zip">
+		</property>
+		<mkdir dir="${distribution}" />
+		<delete>
+			<fileset dir="${distribution}">
+				<include name="*" />
+			</fileset>
+		</delete>
+		<mkdir dir="${distribution}" />
+	</target>
+
+	<target name="javadoc">
+		<javadoc destdir="docs/javadoc" access="package" use="true" notree="false" nonavbar="false" noindex="false" splitindex="true" author="true" version="true" nodeprecatedlist="false" nodeprecated="false" packagenames="de.mcs.utils,de.mcs.utils.codecs" sourcepath="src" classpath="bin" overview="E:\DATEN\SOURCEN\java\MCSUtils\src\overview.html" doctitle="MCSUtils" />
+	</target>
+
+	<!-- delete intermediate files -->
+	<target name="clean" description="delete intermediate files">
+		<delete dir="${dist}" />
+	</target>
+
+	<!-- compile files -->
+	<target name="compile" depends="prepare_properties" description="compile all files">
+		<javac srcdir="${src}" destdir="${distribution}" target="1.6" source="1.6">
+			<classpath>
+				<fileset refid="libraries" />
+				<pathelement location="${src}" />
+			</classpath>
+			<compilerarg value="-deprecation" />
+		</javac>
+	</target>
+
+	<target name="jar src distribute" depends="javadoc,prepare_properties,compile">
+		<jar duplicate="preserve" destfile="${distribution}/${jarname}">
+			<fileset dir="${basedir}/bin" />
+			<manifest>
+				<attribute name="main-version" value="${releasenumber}" />
+				<attribute name="main-build-date" value="${build.date}" />
+				<attribute name="main-vendor" value="MCS" />
+				<attribute name="main-product" value="MCSUtils" />
+			</manifest>
+		</jar>
+		<jar duplicate="preserve" destfile="${distribution}/${app.name}.jar">
+			<fileset dir="${basedir}/bin" />
+			<manifest>
+				<attribute name="main-version" value="${releasenumber}" />
+				<attribute name="main-build-date" value="${build.date}" />
+				<attribute name="main-vendor" value="MCS" />
+				<attribute name="main-product" value="MCSUtils" />
+			</manifest>
+		</jar>
+		<jar duplicate="preserve" destfile="${distribution}/${jarsrcname}">
+			<fileset dir="${basedir}/bin" />
+			<fileset dir="${basedir}/src" />
+			<fileset dir="${basedir}/docs" />
+			<manifest>
+				<attribute name="main-version" value="${releasenumber}" />
+				<attribute name="main-build-date" value="${build.date}" />
+				<attribute name="main-vendor" value="MCS" />
+				<attribute name="main-product" value="MCSUtils" />
+			</manifest>
+		</jar>
+	</target>
+</project>

+ 16 - 0
jar.jardesc

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<jardesc>
+    <jar path="E:/DATEN/SOURCEN/java/MCSUtils/MCSUtils-0.12.17.jar"/>
+    <options buildIfNeeded="true" compress="true" descriptionLocation="/MCSUtils/jar.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="true" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
+    <storedRefactorings deprecationInfo="true" structuralOnly="false"/>
+    <selectedProjects/>
+    <manifest generateManifest="true" manifestLocation="/JMeasurement/src/manifest" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
+        <sealing sealJar="false">
+            <packagesToSeal/>
+            <packagesToUnSeal/>
+        </sealing>
+    </manifest>
+    <selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
+        <javaElement handleIdentifier="=MCSUtils/src"/>
+    </selectedElements>
+</jardesc>

+ 14 - 0
notice.txt

@@ -0,0 +1,14 @@
+
+Copyright 2005 Wilfried Klaas
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 142 - 0
pom.xml

@@ -0,0 +1,142 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>net.sourceforge.jmeasurement2</groupId>
+  <artifactId>MCSUtils</artifactId>
+  <version>1.0.151-SNAPSHOT</version>
+  <name>${project.groupId}:${project.artifactId}</name>
+  <description>Assorted utils for mcs java applications</description>
+  <url>http://www.wk-music.de</url>
+
+  <licenses>
+    <license>
+      <name>The Apache License, Version 2.0</name>
+      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+    </license>
+  </licenses>
+
+  <developers>
+    <developer>
+      <name>Wilfried Klaas</name>
+      <email>w.klaas@gmx.de</email>
+      <organization>MCS</organization>
+      <organizationUrl>http://www.wk-musik.tk</organizationUrl>
+    </developer>
+  </developers>
+
+  <scm>
+    <url>https://wkla.no-ip.biz/svn/MCS/java/MCSUtils/</url>
+    <connection>scm:svn:https://wkla.no-ip.biz/svn/MCS/java/MCSUtils/</connection>
+    <developerConnection>scm:svn:https://wkla.no-ip.biz/svn/MCS/java/MCSUtils/</developerConnection>
+  </scm>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <gpg.skip>false</gpg.skip>
+  </properties>
+
+  <distributionManagement>
+    <snapshotRepository>
+      <id>ossrh</id>
+      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+    </snapshotRepository>
+    <repository>
+      <id>ossrh</id>
+      <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
+    </repository>
+  </distributionManagement>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <version>2.4</version>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>2.10.3</version>
+        <executions>
+          <execution>
+            <id>attach-javadocs</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.3</version>
+        <configuration>
+          <source>1.8</source>
+          <target>1.8</target>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-gpg-plugin</artifactId>
+        <version>1.6</version>
+        <executions>
+          <execution>
+            <id>sign-artifacts</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>sign</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.sonatype.plugins</groupId>
+        <artifactId>nexus-staging-maven-plugin</artifactId>
+        <version>1.6.3</version>
+        <extensions>true</extensions>
+        <configuration>
+          <serverId>ossrh</serverId>
+          <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+          <autoReleaseAfterClose>true</autoReleaseAfterClose>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.12</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit-addons</groupId>
+      <artifactId>junit-addons</artifactId>
+      <version>1.4</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.martiansoftware</groupId>
+      <artifactId>jsap</artifactId>
+      <version>2.1</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.sun.activation</groupId>
+      <artifactId>javax.activation</artifactId>
+      <version>1.2.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.reflections</groupId>
+      <artifactId>reflections</artifactId>
+      <version>0.9.11</version>
+    </dependency>
+  </dependencies>
+</project>

+ 0 - 0
project.index


+ 1714 - 0
src/main/java/com/csvreader/CsvReader.java

@@ -0,0 +1,1714 @@
+/*
+ * Java CSV is a stream based library for reading and writing
+ * CSV and other delimited data.
+ *   
+ * Copyright (C) Bruce Dunwiddie bruce@csvreader.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+package com.csvreader;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.charset.Charset;
+import java.text.NumberFormat;
+import java.util.HashMap;
+
+/**
+ * A stream based parser for parsing delimited text data from a file or a
+ * stream.
+ */
+public class CsvReader {
+  private Reader inputStream = null;
+
+  private String fileName = null;
+
+  // this holds all the values for switches that the user is allowed to set
+  private UserSettings userSettings = new UserSettings();
+
+  private Charset charset = null;
+
+  private boolean useCustomRecordDelimiter = false;
+
+  // this will be our working buffer to hold data chunks
+  // read in from the data file
+
+  private DataBuffer dataBuffer = new DataBuffer();
+
+  private ColumnBuffer columnBuffer = new ColumnBuffer();
+
+  private RawRecordBuffer rawBuffer = new RawRecordBuffer();
+
+  private boolean[] isQualified = null;
+
+  private String rawRecord = "";
+
+  private HeadersHolder headersHolder = new HeadersHolder();
+
+  // these are all more or less global loop variables
+  // to keep from needing to pass them all into various
+  // methods during parsing
+
+  private boolean startedColumn = false;
+
+  private boolean startedWithQualifier = false;
+
+  private boolean hasMoreData = true;
+
+  private char lastLetter = '\0';
+
+  private boolean hasReadNextLine = false;
+
+  private int columnsCount = 0;
+
+  private long currentRecord = 0;
+
+  private String[] values = new String[StaticSettings.INITIAL_COLUMN_COUNT];
+
+  private boolean initialized = false;
+
+  private boolean closed = false;
+
+  /**
+   * Double up the text qualifier to represent an occurance of the text
+   * qualifier.
+   */
+  public static final int ESCAPE_MODE_DOUBLED = 1;
+
+  /**
+   * Use a backslash character before the text qualifier to represent an
+   * occurance of the text qualifier.
+   */
+  public static final int ESCAPE_MODE_BACKSLASH = 2;
+
+  /**
+   * Creates a {@link com.csvreader.CsvReader CsvReader} object using a file as
+   * the data source.
+   * 
+   * @param fileName
+   *          The path to the file to use as the data source.
+   * @param delimiter
+   *          The character to use as the column delimiter.
+   * @param charset
+   *          The {@link java.nio.charset.Charset Charset} to use while parsing
+   *          the data.
+   * @throws FileNotFoundException
+   *           if something goes wrong
+   */
+  public CsvReader(String fileName, char delimiter, Charset charset) throws FileNotFoundException {
+    if (fileName == null) {
+      throw new IllegalArgumentException("Parameter fileName can not be null.");
+    }
+
+    if (charset == null) {
+      throw new IllegalArgumentException("Parameter charset can not be null.");
+    }
+
+    if (!new File(fileName).exists()) {
+      throw new FileNotFoundException("File " + fileName + " does not exist.");
+    }
+
+    this.fileName = fileName;
+    this.userSettings.Delimiter = delimiter;
+    this.charset = charset;
+
+    isQualified = new boolean[values.length];
+  }
+
+  /**
+   * Creates a {@link com.csvreader.CsvReader CsvReader} object using a file as
+   * the data source.&nbsp;Uses ISO-8859-1 as the
+   * {@link java.nio.charset.Charset Charset}.
+   * 
+   * @param fileName
+   *          The path to the file to use as the data source.
+   * @param delimiter
+   *          The character to use as the column delimiter.
+   * @throws FileNotFoundException
+   *           if something goes wrong
+   */
+  public CsvReader(String fileName, char delimiter) throws FileNotFoundException {
+    this(fileName, delimiter, Charset.forName("ISO-8859-1"));
+  }
+
+  /**
+   * Creates a {@link com.csvreader.CsvReader CsvReader} object using a file as
+   * the data source.&nbsp;Uses a comma as the column delimiter and ISO-8859-1
+   * as the {@link java.nio.charset.Charset Charset}.
+   * 
+   * @param fileName
+   *          The path to the file to use as the data source.
+   * @throws FileNotFoundException
+   *           if something goes wrong
+   */
+  public CsvReader(String fileName) throws FileNotFoundException {
+    this(fileName, Letters.COMMA);
+  }
+
+  /**
+   * Constructs a {@link com.csvreader.CsvReader CsvReader} object using a
+   * {@link java.io.Reader Reader} object as the data source.
+   * 
+   * @param inputStream
+   *          The stream to use as the data source.
+   * @param delimiter
+   *          The character to use as the column delimiter.
+   */
+  public CsvReader(Reader inputStream, char delimiter) {
+    if (inputStream == null) {
+      throw new IllegalArgumentException("Parameter inputStream can not be null.");
+    }
+
+    this.inputStream = inputStream;
+    this.userSettings.Delimiter = delimiter;
+    initialized = true;
+
+    isQualified = new boolean[values.length];
+  }
+
+  /**
+   * Constructs a {@link com.csvreader.CsvReader CsvReader} object using a
+   * {@link java.io.Reader Reader} object as the data source.&nbsp;Uses a comma
+   * as the column delimiter.
+   * 
+   * @param inputStream
+   *          The stream to use as the data source.
+   */
+  public CsvReader(Reader inputStream) {
+    this(inputStream, Letters.COMMA);
+  }
+
+  /**
+   * Constructs a {@link com.csvreader.CsvReader CsvReader} object using an
+   * {@link java.io.InputStream InputStream} object as the data source.
+   * 
+   * @param inputStream
+   *          The stream to use as the data source.
+   * @param delimiter
+   *          The character to use as the column delimiter.
+   * @param charset
+   *          The {@link java.nio.charset.Charset Charset} to use while parsing
+   *          the data.
+   */
+  public CsvReader(InputStream inputStream, char delimiter, Charset charset) {
+    this(new InputStreamReader(inputStream, charset), delimiter);
+  }
+
+  /**
+   * Constructs a {@link com.csvreader.CsvReader CsvReader} object using an
+   * {@link java.io.InputStream InputStream} object as the data
+   * source.&nbsp;Uses a comma as the column delimiter.
+   * 
+   * @param inputStream
+   *          The stream to use as the data source.
+   * @param charset
+   *          The {@link java.nio.charset.Charset Charset} to use while parsing
+   *          the data.
+   */
+  public CsvReader(InputStream inputStream, Charset charset) {
+    this(new InputStreamReader(inputStream, charset));
+  }
+
+  public boolean getCaptureRawRecord() {
+    return userSettings.CaptureRawRecord;
+  }
+
+  public void setCaptureRawRecord(boolean captureRawRecord) {
+    userSettings.CaptureRawRecord = captureRawRecord;
+  }
+
+  public String getRawRecord() {
+    return rawRecord;
+  }
+
+  /**
+   * Gets whether leading and trailing whitespace characters are being trimmed
+   * from non-textqualified column data. Default is true.
+   * 
+   * @return Whether leading and trailing whitespace characters are being
+   *         trimmed from non-textqualified column data.
+   */
+  public boolean getTrimWhitespace() {
+    return userSettings.TrimWhitespace;
+  }
+
+  /**
+   * Sets whether leading and trailing whitespace characters should be trimmed
+   * from non-textqualified column data or not. Default is true.
+   * 
+   * @param trimWhitespace
+   *          Whether leading and trailing whitespace characters should be
+   *          trimmed from non-textqualified column data or not.
+   */
+  public void setTrimWhitespace(boolean trimWhitespace) {
+    userSettings.TrimWhitespace = trimWhitespace;
+  }
+
+  /**
+   * Gets the character being used as the column delimiter. Default is comma,
+   * ','.
+   * 
+   * @return The character being used as the column delimiter.
+   */
+  public char getDelimiter() {
+    return userSettings.Delimiter;
+  }
+
+  /**
+   * Sets the character to use as the column delimiter. Default is comma, ','.
+   * 
+   * @param delimiter
+   *          The character to use as the column delimiter.
+   */
+  public void setDelimiter(char delimiter) {
+    userSettings.Delimiter = delimiter;
+  }
+
+  public char getRecordDelimiter() {
+    return userSettings.RecordDelimiter;
+  }
+
+  /**
+   * Sets the character to use as the record delimiter.
+   * 
+   * @param recordDelimiter
+   *          The character to use as the record delimiter. Default is
+   *          combination of standard end of line characters for Windows, Unix,
+   *          or Mac.
+   */
+  public void setRecordDelimiter(char recordDelimiter) {
+    useCustomRecordDelimiter = true;
+    userSettings.RecordDelimiter = recordDelimiter;
+  }
+
+  /**
+   * Gets the character to use as a text qualifier in the data.
+   * 
+   * @return The character to use as a text qualifier in the data.
+   */
+  public char getTextQualifier() {
+    return userSettings.TextQualifier;
+  }
+
+  /**
+   * Sets the character to use as a text qualifier in the data.
+   * 
+   * @param textQualifier
+   *          The character to use as a text qualifier in the data.
+   */
+  public void setTextQualifier(char textQualifier) {
+    userSettings.TextQualifier = textQualifier;
+  }
+
+  /**
+   * Whether text qualifiers will be used while parsing or not.
+   * 
+   * @return Whether text qualifiers will be used while parsing or not.
+   */
+  public boolean getUseTextQualifier() {
+    return userSettings.UseTextQualifier;
+  }
+
+  /**
+   * Sets whether text qualifiers will be used while parsing or not.
+   * 
+   * @param useTextQualifier
+   *          Whether to use a text qualifier while parsing or not.
+   */
+  public void setUseTextQualifier(boolean useTextQualifier) {
+    userSettings.UseTextQualifier = useTextQualifier;
+  }
+
+  /**
+   * Gets the character being used as a comment signal.
+   * 
+   * @return The character being used as a comment signal.
+   */
+  public char getComment() {
+    return userSettings.Comment;
+  }
+
+  /**
+   * Sets the character to use as a comment signal.
+   * 
+   * @param comment
+   *          The character to use as a comment signal.
+   */
+  public void setComment(char comment) {
+    userSettings.Comment = comment;
+  }
+
+  /**
+   * Gets whether comments are being looked for while parsing or not.
+   * 
+   * @return Whether comments are being looked for while parsing or not.
+   */
+  public boolean getUseComments() {
+    return userSettings.UseComments;
+  }
+
+  /**
+   * Sets whether comments are being looked for while parsing or not.
+   * 
+   * @param useComments
+   *          Whether comments are being looked for while parsing or not.
+   */
+  public void setUseComments(boolean useComments) {
+    userSettings.UseComments = useComments;
+  }
+
+  /**
+   * Gets the current way to escape an occurance of the text qualifier inside
+   * qualified data.
+   * 
+   * @return The current way to escape an occurance of the text qualifier inside
+   *         qualified data.
+   */
+  public int getEscapeMode() {
+    return userSettings.EscapeMode;
+  }
+
+  /**
+   * Sets the current way to escape an occurance of the text qualifier inside
+   * qualified data.
+   * 
+   * @param escapeMode
+   *          The way to escape an occurance of the text qualifier inside
+   *          qualified data.
+   * @exception IllegalArgumentException
+   *              When an illegal value is specified for escapeMode.
+   */
+  public void setEscapeMode(int escapeMode) throws IllegalArgumentException {
+    if (escapeMode != ESCAPE_MODE_DOUBLED && escapeMode != ESCAPE_MODE_BACKSLASH) {
+      throw new IllegalArgumentException("Parameter escapeMode must be a valid value.");
+    }
+
+    userSettings.EscapeMode = escapeMode;
+  }
+
+  public boolean getSkipEmptyRecords() {
+    return userSettings.SkipEmptyRecords;
+  }
+
+  public void setSkipEmptyRecords(boolean skipEmptyRecords) {
+    userSettings.SkipEmptyRecords = skipEmptyRecords;
+  }
+
+  /**
+   * Safety caution to prevent the parser from using large amounts of memory in
+   * the case where parsing settings like file encodings don't end up matching
+   * the actual format of a file. This switch can be turned off if the file
+   * format is known and tested. With the switch off, the max column lengths and
+   * max column count per record supported by the parser will greatly increase.
+   * Default is true.
+   * 
+   * @return The current setting of the safety switch.
+   */
+  public boolean getSafetySwitch() {
+    return userSettings.SafetySwitch;
+  }
+
+  /**
+   * Safety caution to prevent the parser from using large amounts of memory in
+   * the case where parsing settings like file encodings don't end up matching
+   * the actual format of a file. This switch can be turned off if the file
+   * format is known and tested. With the switch off, the max column lengths and
+   * max column count per record supported by the parser will greatly increase.
+   * Default is true.
+   * 
+   * @param safetySwitch
+   *          enable or disable memory safety
+   */
+  public void setSafetySwitch(boolean safetySwitch) {
+    userSettings.SafetySwitch = safetySwitch;
+  }
+
+  /**
+   * Gets the count of columns found in this record.
+   * 
+   * @return The count of columns found in this record.
+   */
+  public int getColumnCount() {
+    return columnsCount;
+  }
+
+  /**
+   * Gets the index of the current record.
+   * 
+   * @return The index of the current record.
+   */
+  public long getCurrentRecord() {
+    return currentRecord - 1;
+  }
+
+  /**
+   * Gets the count of headers read in by a previous call to
+   * {@link com.csvreader.CsvReader#readHeaders readHeaders()}.
+   * 
+   * @return The count of headers read in by a previous call to
+   *         {@link com.csvreader.CsvReader#readHeaders readHeaders()}.
+   */
+  public int getHeaderCount() {
+    return headersHolder.Length;
+  }
+
+  /**
+   * Returns the header values as a string array.
+   * 
+   * @return The header values as a String array.
+   * @exception IOException
+   *              Thrown if this object has already been closed.
+   */
+  public String[] getHeaders() throws IOException {
+    checkClosed();
+
+    if (headersHolder.Headers == null) {
+      return null;
+    } else {
+      // use clone here to prevent the outside code from
+      // setting values on the array directly, which would
+      // throw off the index lookup based on header name
+      String[] clone = new String[headersHolder.Length];
+      System.arraycopy(headersHolder.Headers, 0, clone, 0, headersHolder.Length);
+      return clone;
+    }
+  }
+
+  public void setHeaders(String[] headers) {
+    headersHolder.Headers = headers;
+
+    headersHolder.IndexByName.clear();
+
+    if (headers != null) {
+      headersHolder.Length = headers.length;
+    } else {
+      headersHolder.Length = 0;
+    }
+
+    // use headersHolder.Length here in case headers is null
+    for (int i = 0; i < headersHolder.Length; i++) {
+      headersHolder.IndexByName.put(headers[i], new Integer(i));
+    }
+  }
+
+  public String[] getValues() throws IOException {
+    checkClosed();
+
+    // need to return a clone, and can't use clone because values.Length
+    // might be greater than columnsCount
+    String[] clone = new String[columnsCount];
+    System.arraycopy(values, 0, clone, 0, columnsCount);
+    return clone;
+  }
+
+  /**
+   * Returns the current column value for a given column index.
+   * 
+   * @param columnIndex
+   *          The index of the column.
+   * @return The current column value.
+   * @exception IOException
+   *              Thrown if this object has already been closed.
+   */
+  public String get(int columnIndex) throws IOException {
+    checkClosed();
+
+    if (columnIndex > -1 && columnIndex < columnsCount) {
+      return values[columnIndex];
+    } else {
+      return "";
+    }
+  }
+
+  /**
+   * Returns the current column value for a given column header name.
+   * 
+   * @param headerName
+   *          The header name of the column.
+   * @return The current column value.
+   * @exception IOException
+   *              Thrown if this object has already been closed.
+   */
+  public String get(String headerName) throws IOException {
+    checkClosed();
+
+    return get(getIndex(headerName));
+  }
+
+  /**
+   * Creates a {@link com.csvreader.CsvReader CsvReader} object using a string
+   * of data as the source.&nbsp;Uses ISO-8859-1 as the
+   * {@link java.nio.charset.Charset Charset}.
+   * 
+   * @param data
+   *          The String of data to use as the source.
+   * @return A {@link com.csvreader.CsvReader CsvReader} object using the String
+   *         of data as the source.
+   */
+  public static CsvReader parse(String data) {
+    if (data == null) {
+      throw new IllegalArgumentException("Parameter data can not be null.");
+    }
+
+    return new CsvReader(new StringReader(data));
+  }
+
+  /**
+   * Reads another record.
+   * 
+   * @return Whether another record was successfully read or not.
+   * @exception IOException
+   *              Thrown if an error occurs while reading data from the source
+   *              stream.
+   */
+  public boolean readRecord() throws IOException {
+    checkClosed();
+
+    columnsCount = 0;
+    rawBuffer.Position = 0;
+
+    dataBuffer.LineStart = dataBuffer.Position;
+
+    hasReadNextLine = false;
+
+    // check to see if we've already found the end of data
+
+    if (hasMoreData) {
+      // loop over the data stream until the end of data is found
+      // or the end of the record is found
+
+      do {
+        if (dataBuffer.Position == dataBuffer.Count) {
+          checkDataLength();
+        } else {
+          startedWithQualifier = false;
+
+          // grab the current letter as a char
+
+          char currentLetter = dataBuffer.Buffer[dataBuffer.Position];
+
+          if (userSettings.UseTextQualifier && currentLetter == userSettings.TextQualifier) {
+            // this will be a text qualified column, so
+            // we need to set startedWithQualifier to make it
+            // enter the seperate branch to handle text
+            // qualified columns
+
+            lastLetter = currentLetter;
+
+            // read qualified
+            startedColumn = true;
+            dataBuffer.ColumnStart = dataBuffer.Position + 1;
+            startedWithQualifier = true;
+            boolean lastLetterWasQualifier = false;
+
+            char escapeChar = userSettings.TextQualifier;
+
+            if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH) {
+              escapeChar = Letters.BACKSLASH;
+            }
+
+            boolean eatingTrailingJunk = false;
+            boolean lastLetterWasEscape = false;
+            boolean readingComplexEscape = false;
+            int escape = ComplexEscape.UNICODE;
+            int escapeLength = 0;
+            char escapeValue = (char) 0;
+
+            dataBuffer.Position++;
+
+            do {
+              if (dataBuffer.Position == dataBuffer.Count) {
+                checkDataLength();
+              } else {
+                // grab the current letter as a char
+
+                currentLetter = dataBuffer.Buffer[dataBuffer.Position];
+
+                if (eatingTrailingJunk) {
+                  dataBuffer.ColumnStart = dataBuffer.Position + 1;
+
+                  if (currentLetter == userSettings.Delimiter) {
+                    endColumn();
+                  } else if ((!useCustomRecordDelimiter && (currentLetter == Letters.CR || currentLetter == Letters.LF))
+                      || (useCustomRecordDelimiter && currentLetter == userSettings.RecordDelimiter)) {
+                    endColumn();
+
+                    endRecord();
+                  }
+                } else if (readingComplexEscape) {
+                  escapeLength++;
+
+                  switch (escape) {
+                  case ComplexEscape.UNICODE:
+                    escapeValue *= (char) 16;
+                    escapeValue += hexToDec(currentLetter);
+
+                    if (escapeLength == 4) {
+                      readingComplexEscape = false;
+                    }
+
+                    break;
+                  case ComplexEscape.OCTAL:
+                    escapeValue *= (char) 8;
+                    escapeValue += (char) (currentLetter - '0');
+
+                    if (escapeLength == 3) {
+                      readingComplexEscape = false;
+                    }
+
+                    break;
+                  case ComplexEscape.DECIMAL:
+                    escapeValue *= (char) 10;
+                    escapeValue += (char) (currentLetter - '0');
+
+                    if (escapeLength == 3) {
+                      readingComplexEscape = false;
+                    }
+
+                    break;
+                  case ComplexEscape.HEX:
+                    escapeValue *= (char) 16;
+                    escapeValue += hexToDec(currentLetter);
+
+                    if (escapeLength == 2) {
+                      readingComplexEscape = false;
+                    }
+
+                    break;
+                  }
+
+                  if (!readingComplexEscape) {
+                    appendLetter(escapeValue);
+                  } else {
+                    dataBuffer.ColumnStart = dataBuffer.Position + 1;
+                  }
+                } else if (currentLetter == userSettings.TextQualifier) {
+                  if (lastLetterWasEscape) {
+                    lastLetterWasEscape = false;
+                    lastLetterWasQualifier = false;
+                  } else {
+                    updateCurrentValue();
+
+                    if (userSettings.EscapeMode == ESCAPE_MODE_DOUBLED) {
+                      lastLetterWasEscape = true;
+                    }
+
+                    lastLetterWasQualifier = true;
+                  }
+                } else if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH && lastLetterWasEscape) {
+                  switch (currentLetter) {
+                  case 'n':
+                    appendLetter(Letters.LF);
+                    break;
+                  case 'r':
+                    appendLetter(Letters.CR);
+                    break;
+                  case 't':
+                    appendLetter(Letters.TAB);
+                    break;
+                  case 'b':
+                    appendLetter(Letters.BACKSPACE);
+                    break;
+                  case 'f':
+                    appendLetter(Letters.FORM_FEED);
+                    break;
+                  case 'e':
+                    appendLetter(Letters.ESCAPE);
+                    break;
+                  case 'v':
+                    appendLetter(Letters.VERTICAL_TAB);
+                    break;
+                  case 'a':
+                    appendLetter(Letters.ALERT);
+                    break;
+                  case '0':
+                  case '1':
+                  case '2':
+                  case '3':
+                  case '4':
+                  case '5':
+                  case '6':
+                  case '7':
+                    escape = ComplexEscape.OCTAL;
+                    readingComplexEscape = true;
+                    escapeLength = 1;
+                    escapeValue = (char) (currentLetter - '0');
+                    dataBuffer.ColumnStart = dataBuffer.Position + 1;
+                    break;
+                  case 'u':
+                  case 'x':
+                  case 'o':
+                  case 'd':
+                  case 'U':
+                  case 'X':
+                  case 'O':
+                  case 'D':
+                    switch (currentLetter) {
+                    case 'u':
+                    case 'U':
+                      escape = ComplexEscape.UNICODE;
+                      break;
+                    case 'x':
+                    case 'X':
+                      escape = ComplexEscape.HEX;
+                      break;
+                    case 'o':
+                    case 'O':
+                      escape = ComplexEscape.OCTAL;
+                      break;
+                    case 'd':
+                    case 'D':
+                      escape = ComplexEscape.DECIMAL;
+                      break;
+                    }
+
+                    readingComplexEscape = true;
+                    escapeLength = 0;
+                    escapeValue = (char) 0;
+                    dataBuffer.ColumnStart = dataBuffer.Position + 1;
+
+                    break;
+                  default:
+                    break;
+                  }
+
+                  lastLetterWasEscape = false;
+
+                  // can only happen for ESCAPE_MODE_BACKSLASH
+                } else if (currentLetter == escapeChar) {
+                  updateCurrentValue();
+                  lastLetterWasEscape = true;
+                } else {
+                  if (lastLetterWasQualifier) {
+                    if (currentLetter == userSettings.Delimiter) {
+                      endColumn();
+                    } else
+                      if ((!useCustomRecordDelimiter && (currentLetter == Letters.CR || currentLetter == Letters.LF))
+                          || (useCustomRecordDelimiter && currentLetter == userSettings.RecordDelimiter)) {
+                      endColumn();
+
+                      endRecord();
+                    } else {
+                      dataBuffer.ColumnStart = dataBuffer.Position + 1;
+
+                      eatingTrailingJunk = true;
+                    }
+
+                    // make sure to clear the flag for next
+                    // run of the loop
+
+                    lastLetterWasQualifier = false;
+                  }
+                }
+
+                // keep track of the last letter because we need
+                // it for several key decisions
+
+                lastLetter = currentLetter;
+
+                if (startedColumn) {
+                  dataBuffer.Position++;
+
+                  if (userSettings.SafetySwitch
+                      && dataBuffer.Position - dataBuffer.ColumnStart + columnBuffer.Position > 100000) {
+                    close();
+
+                    throw new IOException("Maximum column length of 100,000 exceeded in column "
+                        + NumberFormat.getIntegerInstance().format(columnsCount) + " in record "
+                        + NumberFormat.getIntegerInstance().format(currentRecord)
+                        + ". Set the SafetySwitch property to false"
+                        + " if you're expecting column lengths greater than 100,000 characters to"
+                        + " avoid this error.");
+                  }
+                }
+              } // end else
+
+            } while (hasMoreData && startedColumn);
+          } else if (currentLetter == userSettings.Delimiter) {
+            // we encountered a column with no data, so
+            // just send the end column
+
+            lastLetter = currentLetter;
+
+            endColumn();
+          } else if (useCustomRecordDelimiter && currentLetter == userSettings.RecordDelimiter) {
+            // this will skip blank lines
+            if (startedColumn || columnsCount > 0 || !userSettings.SkipEmptyRecords) {
+              endColumn();
+
+              endRecord();
+            } else {
+              dataBuffer.LineStart = dataBuffer.Position + 1;
+            }
+
+            lastLetter = currentLetter;
+          } else if (!useCustomRecordDelimiter && (currentLetter == Letters.CR || currentLetter == Letters.LF)) {
+            // this will skip blank lines
+            if (startedColumn || columnsCount > 0
+                || (!userSettings.SkipEmptyRecords && (currentLetter == Letters.CR || lastLetter != Letters.CR))) {
+              endColumn();
+
+              endRecord();
+            } else {
+              dataBuffer.LineStart = dataBuffer.Position + 1;
+            }
+
+            lastLetter = currentLetter;
+          } else if (userSettings.UseComments && columnsCount == 0 && currentLetter == userSettings.Comment) {
+            // encountered a comment character at the beginning of
+            // the line so just ignore the rest of the line
+
+            lastLetter = currentLetter;
+
+            skipLine();
+          } else if (userSettings.TrimWhitespace && (currentLetter == Letters.SPACE || currentLetter == Letters.TAB)) {
+            // do nothing, this will trim leading whitespace
+            // for both text qualified columns and non
+
+            startedColumn = true;
+            dataBuffer.ColumnStart = dataBuffer.Position + 1;
+          } else {
+            // since the letter wasn't a special letter, this
+            // will be the first letter of our current column
+
+            startedColumn = true;
+            dataBuffer.ColumnStart = dataBuffer.Position;
+            boolean lastLetterWasBackslash = false;
+            boolean readingComplexEscape = false;
+            int escape = ComplexEscape.UNICODE;
+            int escapeLength = 0;
+            char escapeValue = (char) 0;
+
+            boolean firstLoop = true;
+
+            do {
+              if (!firstLoop && dataBuffer.Position == dataBuffer.Count) {
+                checkDataLength();
+              } else {
+                if (!firstLoop) {
+                  // grab the current letter as a char
+                  currentLetter = dataBuffer.Buffer[dataBuffer.Position];
+                }
+
+                if (!userSettings.UseTextQualifier && userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH
+                    && currentLetter == Letters.BACKSLASH) {
+                  if (lastLetterWasBackslash) {
+                    lastLetterWasBackslash = false;
+                  } else {
+                    updateCurrentValue();
+                    lastLetterWasBackslash = true;
+                  }
+                } else if (readingComplexEscape) {
+                  escapeLength++;
+
+                  switch (escape) {
+                  case ComplexEscape.UNICODE:
+                    escapeValue *= (char) 16;
+                    escapeValue += hexToDec(currentLetter);
+
+                    if (escapeLength == 4) {
+                      readingComplexEscape = false;
+                    }
+
+                    break;
+                  case ComplexEscape.OCTAL:
+                    escapeValue *= (char) 8;
+                    escapeValue += (char) (currentLetter - '0');
+
+                    if (escapeLength == 3) {
+                      readingComplexEscape = false;
+                    }
+
+                    break;
+                  case ComplexEscape.DECIMAL:
+                    escapeValue *= (char) 10;
+                    escapeValue += (char) (currentLetter - '0');
+
+                    if (escapeLength == 3) {
+                      readingComplexEscape = false;
+                    }
+
+                    break;
+                  case ComplexEscape.HEX:
+                    escapeValue *= (char) 16;
+                    escapeValue += hexToDec(currentLetter);
+
+                    if (escapeLength == 2) {
+                      readingComplexEscape = false;
+                    }
+
+                    break;
+                  }
+
+                  if (!readingComplexEscape) {
+                    appendLetter(escapeValue);
+                  } else {
+                    dataBuffer.ColumnStart = dataBuffer.Position + 1;
+                  }
+                } else if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH && lastLetterWasBackslash) {
+                  switch (currentLetter) {
+                  case 'n':
+                    appendLetter(Letters.LF);
+                    break;
+                  case 'r':
+                    appendLetter(Letters.CR);
+                    break;
+                  case 't':
+                    appendLetter(Letters.TAB);
+                    break;
+                  case 'b':
+                    appendLetter(Letters.BACKSPACE);
+                    break;
+                  case 'f':
+                    appendLetter(Letters.FORM_FEED);
+                    break;
+                  case 'e':
+                    appendLetter(Letters.ESCAPE);
+                    break;
+                  case 'v':
+                    appendLetter(Letters.VERTICAL_TAB);
+                    break;
+                  case 'a':
+                    appendLetter(Letters.ALERT);
+                    break;
+                  case '0':
+                  case '1':
+                  case '2':
+                  case '3':
+                  case '4':
+                  case '5':
+                  case '6':
+                  case '7':
+                    escape = ComplexEscape.OCTAL;
+                    readingComplexEscape = true;
+                    escapeLength = 1;
+                    escapeValue = (char) (currentLetter - '0');
+                    dataBuffer.ColumnStart = dataBuffer.Position + 1;
+                    break;
+                  case 'u':
+                  case 'x':
+                  case 'o':
+                  case 'd':
+                  case 'U':
+                  case 'X':
+                  case 'O':
+                  case 'D':
+                    switch (currentLetter) {
+                    case 'u':
+                    case 'U':
+                      escape = ComplexEscape.UNICODE;
+                      break;
+                    case 'x':
+                    case 'X':
+                      escape = ComplexEscape.HEX;
+                      break;
+                    case 'o':
+                    case 'O':
+                      escape = ComplexEscape.OCTAL;
+                      break;
+                    case 'd':
+                    case 'D':
+                      escape = ComplexEscape.DECIMAL;
+                      break;
+                    }
+
+                    readingComplexEscape = true;
+                    escapeLength = 0;
+                    escapeValue = (char) 0;
+                    dataBuffer.ColumnStart = dataBuffer.Position + 1;
+
+                    break;
+                  default:
+                    break;
+                  }
+
+                  lastLetterWasBackslash = false;
+                } else {
+                  if (currentLetter == userSettings.Delimiter) {
+                    endColumn();
+                  } else if ((!useCustomRecordDelimiter && (currentLetter == Letters.CR || currentLetter == Letters.LF))
+                      || (useCustomRecordDelimiter && currentLetter == userSettings.RecordDelimiter)) {
+                    endColumn();
+
+                    endRecord();
+                  }
+                }
+
+                // keep track of the last letter because we need
+                // it for several key decisions
+
+                lastLetter = currentLetter;
+                firstLoop = false;
+
+                if (startedColumn) {
+                  dataBuffer.Position++;
+
+                  if (userSettings.SafetySwitch
+                      && dataBuffer.Position - dataBuffer.ColumnStart + columnBuffer.Position > 100000) {
+                    close();
+
+                    throw new IOException("Maximum column length of 100,000 exceeded in column "
+                        + NumberFormat.getIntegerInstance().format(columnsCount) + " in record "
+                        + NumberFormat.getIntegerInstance().format(currentRecord)
+                        + ". Set the SafetySwitch property to false"
+                        + " if you're expecting column lengths greater than 100,000 characters to"
+                        + " avoid this error.");
+                  }
+                }
+              } // end else
+            } while (hasMoreData && startedColumn);
+          }
+
+          if (hasMoreData) {
+            dataBuffer.Position++;
+          }
+        } // end else
+      } while (hasMoreData && !hasReadNextLine);
+
+      // check to see if we hit the end of the file
+      // without processing the current record
+
+      if (startedColumn || lastLetter == userSettings.Delimiter) {
+        endColumn();
+
+        endRecord();
+      }
+    }
+
+    if (userSettings.CaptureRawRecord) {
+      if (hasMoreData) {
+        if (rawBuffer.Position == 0) {
+          rawRecord = new String(dataBuffer.Buffer, dataBuffer.LineStart,
+              dataBuffer.Position - dataBuffer.LineStart - 1);
+        } else {
+          rawRecord = new String(rawBuffer.Buffer, 0, rawBuffer.Position)
+              + new String(dataBuffer.Buffer, dataBuffer.LineStart, dataBuffer.Position - dataBuffer.LineStart - 1);
+        }
+      } else {
+        // for hasMoreData to ever be false, all data would have had to
+        // have been
+        // copied to the raw buffer
+        rawRecord = new String(rawBuffer.Buffer, 0, rawBuffer.Position);
+      }
+    } else {
+      rawRecord = "";
+    }
+
+    return hasReadNextLine;
+  }
+
+  /**
+   * @exception IOException
+   *              Thrown if an error occurs while reading data from the source
+   *              stream.
+   */
+  private void checkDataLength() throws IOException {
+    if (!initialized) {
+      if (fileName != null) {
+        inputStream = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), charset),
+            StaticSettings.MAX_FILE_BUFFER_SIZE);
+      }
+
+      charset = null;
+      initialized = true;
+    }
+
+    updateCurrentValue();
+
+    if (userSettings.CaptureRawRecord && dataBuffer.Count > 0) {
+      if (rawBuffer.Buffer.length - rawBuffer.Position < dataBuffer.Count - dataBuffer.LineStart) {
+        int newLength = rawBuffer.Buffer.length
+            + Math.max(dataBuffer.Count - dataBuffer.LineStart, rawBuffer.Buffer.length);
+
+        char[] holder = new char[newLength];
+
+        System.arraycopy(rawBuffer.Buffer, 0, holder, 0, rawBuffer.Position);
+
+        rawBuffer.Buffer = holder;
+      }
+
+      System.arraycopy(dataBuffer.Buffer, dataBuffer.LineStart, rawBuffer.Buffer, rawBuffer.Position,
+          dataBuffer.Count - dataBuffer.LineStart);
+
+      rawBuffer.Position += dataBuffer.Count - dataBuffer.LineStart;
+    }
+
+    try {
+      dataBuffer.Count = inputStream.read(dataBuffer.Buffer, 0, dataBuffer.Buffer.length);
+    } catch (IOException ex) {
+      close();
+
+      throw ex;
+    }
+
+    // if no more data could be found, set flag stating that
+    // the end of the data was found
+
+    if (dataBuffer.Count == -1) {
+      hasMoreData = false;
+    }
+
+    dataBuffer.Position = 0;
+    dataBuffer.LineStart = 0;
+    dataBuffer.ColumnStart = 0;
+  }
+
+  /**
+   * Read the first record of data as column headers.
+   * 
+   * @return Whether the header record was successfully read or not.
+   * @exception IOException
+   *              Thrown if an error occurs while reading data from the source
+   *              stream.
+   */
+  public boolean readHeaders() throws IOException {
+    boolean result = readRecord();
+
+    // copy the header data from the column array
+    // to the header string array
+
+    headersHolder.Length = columnsCount;
+
+    headersHolder.Headers = new String[columnsCount];
+
+    for (int i = 0; i < headersHolder.Length; i++) {
+      String columnValue = get(i);
+
+      headersHolder.Headers[i] = columnValue;
+
+      // if there are duplicate header names, we will save the last one
+      headersHolder.IndexByName.put(columnValue, new Integer(i));
+    }
+
+    if (result) {
+      currentRecord--;
+    }
+
+    columnsCount = 0;
+
+    return result;
+  }
+
+  /**
+   * Returns the column header value for a given column index.
+   * 
+   * @param columnIndex
+   *          The index of the header column being requested.
+   * @return The value of the column header at the given column index.
+   * @exception IOException
+   *              Thrown if this object has already been closed.
+   */
+  public String getHeader(int columnIndex) throws IOException {
+    checkClosed();
+
+    // check to see if we have read the header record yet
+
+    // check to see if the column index is within the bounds
+    // of our header array
+
+    if (columnIndex > -1 && columnIndex < headersHolder.Length) {
+      // return the processed header data for this column
+
+      return headersHolder.Headers[columnIndex];
+    } else {
+      return "";
+    }
+  }
+
+  public boolean isQualified(int columnIndex) throws IOException {
+    checkClosed();
+
+    if (columnIndex < columnsCount && columnIndex > -1) {
+      return isQualified[columnIndex];
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * @exception IOException
+   *              Thrown if a very rare extreme exception occurs during parsing,
+   *              normally resulting from improper data format.
+   */
+  private void endColumn() throws IOException {
+    String currentValue = "";
+
+    // must be called before setting startedColumn = false
+    if (startedColumn) {
+      if (columnBuffer.Position == 0) {
+        if (dataBuffer.ColumnStart < dataBuffer.Position) {
+          int lastLetter = dataBuffer.Position - 1;
+
+          if (userSettings.TrimWhitespace && !startedWithQualifier) {
+            while (lastLetter >= dataBuffer.ColumnStart
+                && (dataBuffer.Buffer[lastLetter] == Letters.SPACE || dataBuffer.Buffer[lastLetter] == Letters.TAB)) {
+              lastLetter--;
+            }
+          }
+
+          currentValue = new String(dataBuffer.Buffer, dataBuffer.ColumnStart, lastLetter - dataBuffer.ColumnStart + 1);
+        }
+      } else {
+        updateCurrentValue();
+
+        int lastLetter = columnBuffer.Position - 1;
+
+        if (userSettings.TrimWhitespace && !startedWithQualifier) {
+          while (lastLetter >= 0 && (columnBuffer.Buffer[lastLetter] == Letters.SPACE
+              || columnBuffer.Buffer[lastLetter] == Letters.SPACE)) {
+            lastLetter--;
+          }
+        }
+
+        currentValue = new String(columnBuffer.Buffer, 0, lastLetter + 1);
+      }
+    }
+
+    columnBuffer.Position = 0;
+
+    startedColumn = false;
+
+    if (columnsCount >= 100000 && userSettings.SafetySwitch) {
+      close();
+
+      throw new IOException("Maximum column count of 100,000 exceeded in record "
+          + NumberFormat.getIntegerInstance().format(currentRecord) + ". Set the SafetySwitch property to false"
+          + " if you're expecting more than 100,000 columns per record to" + " avoid this error.");
+    }
+
+    // check to see if our current holder array for
+    // column chunks is still big enough to handle another
+    // column chunk
+
+    if (columnsCount == values.length) {
+      // holder array needs to grow to be able to hold another column
+      int newLength = values.length * 2;
+
+      String[] holder = new String[newLength];
+
+      System.arraycopy(values, 0, holder, 0, values.length);
+
+      values = holder;
+
+      boolean[] qualifiedHolder = new boolean[newLength];
+
+      System.arraycopy(isQualified, 0, qualifiedHolder, 0, isQualified.length);
+
+      isQualified = qualifiedHolder;
+    }
+
+    values[columnsCount] = currentValue;
+
+    isQualified[columnsCount] = startedWithQualifier;
+
+    currentValue = "";
+
+    columnsCount++;
+  }
+
+  private void appendLetter(char letter) {
+    if (columnBuffer.Position == columnBuffer.Buffer.length) {
+      int newLength = columnBuffer.Buffer.length * 2;
+
+      char[] holder = new char[newLength];
+
+      System.arraycopy(columnBuffer.Buffer, 0, holder, 0, columnBuffer.Position);
+
+      columnBuffer.Buffer = holder;
+    }
+    columnBuffer.Buffer[columnBuffer.Position++] = letter;
+    dataBuffer.ColumnStart = dataBuffer.Position + 1;
+  }
+
+  private void updateCurrentValue() {
+    if (startedColumn && dataBuffer.ColumnStart < dataBuffer.Position) {
+      if (columnBuffer.Buffer.length - columnBuffer.Position < dataBuffer.Position - dataBuffer.ColumnStart) {
+        int newLength = columnBuffer.Buffer.length
+            + Math.max(dataBuffer.Position - dataBuffer.ColumnStart, columnBuffer.Buffer.length);
+
+        char[] holder = new char[newLength];
+
+        System.arraycopy(columnBuffer.Buffer, 0, holder, 0, columnBuffer.Position);
+
+        columnBuffer.Buffer = holder;
+      }
+
+      System.arraycopy(dataBuffer.Buffer, dataBuffer.ColumnStart, columnBuffer.Buffer, columnBuffer.Position,
+          dataBuffer.Position - dataBuffer.ColumnStart);
+
+      columnBuffer.Position += dataBuffer.Position - dataBuffer.ColumnStart;
+    }
+
+    dataBuffer.ColumnStart = dataBuffer.Position + 1;
+  }
+
+  /**
+   * @exception IOException
+   *              Thrown if an error occurs while reading data from the source
+   *              stream.
+   */
+  private void endRecord() throws IOException {
+    // this flag is used as a loop exit condition
+    // during parsing
+
+    hasReadNextLine = true;
+
+    currentRecord++;
+  }
+
+  /**
+   * Gets the corresponding column index for a given column header name.
+   * 
+   * @param headerName
+   *          The header name of the column.
+   * @return The column index for the given column header name.&nbsp;Returns -1
+   *         if not found.
+   * @exception IOException
+   *              Thrown if this object has already been closed.
+   */
+  public int getIndex(String headerName) throws IOException {
+    checkClosed();
+
+    Integer indexValue = headersHolder.IndexByName.get(headerName);
+
+    if (indexValue != null) {
+      return indexValue.intValue();
+    } else {
+      return -1;
+    }
+  }
+
+  /**
+   * Skips the next record of data by parsing each column.&nbsp;Does not
+   * increment {@link com.csvreader.CsvReader#getCurrentRecord
+   * getCurrentRecord()}.
+   * 
+   * @return Whether another record was successfully skipped or not.
+   * @exception IOException
+   *              Thrown if an error occurs while reading data from the source
+   *              stream.
+   */
+  public boolean skipRecord() throws IOException {
+    checkClosed();
+
+    boolean recordRead = false;
+
+    if (hasMoreData) {
+      recordRead = readRecord();
+
+      if (recordRead) {
+        currentRecord--;
+      }
+    }
+
+    return recordRead;
+  }
+
+  /**
+   * Skips the next line of data using the standard end of line characters and
+   * does not do any column delimited parsing.
+   * 
+   * @return Whether a line was successfully skipped or not.
+   * @exception IOException
+   *              Thrown if an error occurs while reading data from the source
+   *              stream.
+   */
+  public boolean skipLine() throws IOException {
+    checkClosed();
+
+    // clear public column values for current line
+
+    columnsCount = 0;
+
+    boolean skippedLine = false;
+
+    if (hasMoreData) {
+      boolean foundEol = false;
+
+      do {
+        if (dataBuffer.Position == dataBuffer.Count) {
+          checkDataLength();
+        } else {
+          skippedLine = true;
+
+          // grab the current letter as a char
+
+          char currentLetter = dataBuffer.Buffer[dataBuffer.Position];
+
+          if (currentLetter == Letters.CR || currentLetter == Letters.LF) {
+            foundEol = true;
+          }
+
+          // keep track of the last letter because we need
+          // it for several key decisions
+
+          lastLetter = currentLetter;
+
+          if (!foundEol) {
+            dataBuffer.Position++;
+          }
+
+        } // end else
+      } while (hasMoreData && !foundEol);
+
+      columnBuffer.Position = 0;
+
+      dataBuffer.LineStart = dataBuffer.Position + 1;
+    }
+
+    rawBuffer.Position = 0;
+    rawRecord = "";
+
+    return skippedLine;
+  }
+
+  /**
+   * Closes and releases all related resources.
+   */
+  public void close() {
+    if (!closed) {
+      close(true);
+
+      closed = true;
+    }
+  }
+
+  /**
+   * 
+   */
+  private void close(boolean closing) {
+    if (!closed) {
+      if (closing) {
+        charset = null;
+        headersHolder.Headers = null;
+        headersHolder.IndexByName = null;
+        dataBuffer.Buffer = null;
+        columnBuffer.Buffer = null;
+        rawBuffer.Buffer = null;
+      }
+
+      try {
+        if (initialized) {
+          inputStream.close();
+        }
+      } catch (Exception e) {
+        // just eat the exception
+      }
+
+      inputStream = null;
+
+      closed = true;
+    }
+  }
+
+  /**
+   * @exception IOException
+   *              Thrown if this object has already been closed.
+   */
+  private void checkClosed() throws IOException {
+    if (closed) {
+      throw new IOException("This instance of the CsvReader class has already been closed.");
+    }
+  }
+
+  /**
+   * 
+   */
+  protected void finalize() {
+    close(false);
+  }
+
+  private class ComplexEscape {
+    private static final int UNICODE = 1;
+
+    private static final int OCTAL = 2;
+
+    private static final int DECIMAL = 3;
+
+    private static final int HEX = 4;
+  }
+
+  private static char hexToDec(char hex) {
+    char result;
+
+    if (hex >= 'a') {
+      result = (char) (hex - 'a' + 10);
+    } else if (hex >= 'A') {
+      result = (char) (hex - 'A' + 10);
+    } else {
+      result = (char) (hex - '0');
+    }
+
+    return result;
+  }
+
+  private class DataBuffer {
+    public char[] Buffer;
+
+    public int Position;
+
+    // / <summary>
+    // / How much usable data has been read into the stream,
+    // / which will not always be as long as Buffer.Length.
+    // / </summary>
+    public int Count;
+
+    // / <summary>
+    // / The position of the cursor in the buffer when the
+    // / current column was started or the last time data
+    // / was moved out to the column buffer.
+    // / </summary>
+    public int ColumnStart;
+
+    public int LineStart;
+
+    public DataBuffer() {
+      Buffer = new char[StaticSettings.MAX_BUFFER_SIZE];
+      Position = 0;
+      Count = 0;
+      ColumnStart = 0;
+      LineStart = 0;
+    }
+  }
+
+  private class ColumnBuffer {
+    public char[] Buffer;
+
+    public int Position;
+
+    public ColumnBuffer() {
+      Buffer = new char[StaticSettings.INITIAL_COLUMN_BUFFER_SIZE];
+      Position = 0;
+    }
+  }
+
+  private class RawRecordBuffer {
+    public char[] Buffer;
+
+    public int Position;
+
+    public RawRecordBuffer() {
+      Buffer = new char[StaticSettings.INITIAL_COLUMN_BUFFER_SIZE * StaticSettings.INITIAL_COLUMN_COUNT];
+      Position = 0;
+    }
+  }
+
+  private class Letters {
+    public static final char LF = '\n';
+
+    public static final char CR = '\r';
+
+    public static final char QUOTE = '"';
+
+    public static final char COMMA = ',';
+
+    public static final char SPACE = ' ';
+
+    public static final char TAB = '\t';
+
+    public static final char POUND = '#';
+
+    public static final char BACKSLASH = '\\';
+
+    public static final char NULL = '\0';
+
+    public static final char BACKSPACE = '\b';
+
+    public static final char FORM_FEED = '\f';
+
+    public static final char ESCAPE = '\u001B'; // ASCII/ANSI escape
+
+    public static final char VERTICAL_TAB = '\u000B';
+
+    public static final char ALERT = '\u0007';
+  }
+
+  private class UserSettings {
+    // having these as publicly accessible members will prevent
+    // the overhead of the method call that exists on properties
+    // public boolean CaseSensitive;
+
+    public char TextQualifier;
+
+    public boolean TrimWhitespace;
+
+    public boolean UseTextQualifier;
+
+    public char Delimiter;
+
+    public char RecordDelimiter;
+
+    public char Comment;
+
+    public boolean UseComments;
+
+    public int EscapeMode;
+
+    public boolean SafetySwitch;
+
+    public boolean SkipEmptyRecords;
+
+    public boolean CaptureRawRecord;
+
+    public UserSettings() {
+      // CaseSensitive = true;
+      TextQualifier = Letters.QUOTE;
+      TrimWhitespace = true;
+      UseTextQualifier = true;
+      Delimiter = Letters.COMMA;
+      RecordDelimiter = Letters.NULL;
+      Comment = Letters.POUND;
+      UseComments = false;
+      EscapeMode = CsvReader.ESCAPE_MODE_DOUBLED;
+      SafetySwitch = true;
+      SkipEmptyRecords = true;
+      CaptureRawRecord = true;
+    }
+  }
+
+  private class HeadersHolder {
+    public String[] Headers;
+
+    public int Length;
+
+    public HashMap<String, Integer> IndexByName;
+
+    public HeadersHolder() {
+      Headers = null;
+      Length = 0;
+      IndexByName = new HashMap<String, Integer>();
+    }
+  }
+
+  private class StaticSettings {
+    // these are static instead of final so they can be changed in unit test
+    // isn't visible outside this class and is only accessed once during
+    // CsvReader construction
+    public static final int MAX_BUFFER_SIZE = 1024;
+
+    public static final int MAX_FILE_BUFFER_SIZE = 4 * 1024;
+
+    public static final int INITIAL_COLUMN_COUNT = 10;
+
+    public static final int INITIAL_COLUMN_BUFFER_SIZE = 50;
+  }
+}

+ 599 - 0
src/main/java/com/csvreader/CsvWriter.java

@@ -0,0 +1,599 @@
+/*
+ * Java CSV is a stream based library for reading and writing
+ * CSV and other delimited data.
+ *   
+ * Copyright (C) Bruce Dunwiddie bruce@csvreader.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+package com.csvreader;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+
+/**
+ * A stream based writer for writing delimited text data to a file or a stream.
+ */
+public class CsvWriter {
+	private PrintWriter outputStream = null;
+
+	private String fileName = null;
+
+	private boolean firstColumn = true;
+
+	private boolean useCustomRecordDelimiter = false;
+
+	private Charset charset = null;
+
+	// this holds all the values for switches that the user is allowed to set
+	private UserSettings userSettings = new UserSettings();
+
+	private boolean initialized = false;
+
+	private boolean closed = false;
+
+	/**
+	 * Double up the text qualifier to represent an occurance of the text
+	 * qualifier.
+	 */
+	public static final int ESCAPE_MODE_DOUBLED = 1;
+
+	/**
+	 * Use a backslash character before the text qualifier to represent an
+	 * occurance of the text qualifier.
+	 */
+	public static final int ESCAPE_MODE_BACKSLASH = 2;
+
+	/**
+	 * Creates a {@link com.csvreader.CsvWriter CsvWriter} object using a file
+	 * as the data destination.
+	 * 
+	 * @param fileName
+	 *            The path to the file to output the data.
+	 * @param delimiter
+	 *            The character to use as the column delimiter.
+	 * @param charset
+	 *            The {@link java.nio.charset.Charset Charset} to use while
+	 *            writing the data.
+	 */
+	public CsvWriter(String fileName, char delimiter, Charset charset) {
+		if (fileName == null) {
+			throw new IllegalArgumentException("Parameter fileName can not be null.");
+		}
+
+		if (charset == null) {
+			throw new IllegalArgumentException("Parameter charset can not be null.");
+		}
+
+		this.fileName = fileName;
+		userSettings.Delimiter = delimiter;
+		this.charset = charset;
+	}
+
+	/**
+	 * Creates a {@link com.csvreader.CsvWriter CsvWriter} object using a file
+	 * as the data destination.&nbsp;Uses a comma as the column delimiter and
+	 * ISO-8859-1 as the {@link java.nio.charset.Charset Charset}.
+	 * 
+	 * @param fileName
+	 *            The path to the file to output the data.
+	 */
+	public CsvWriter(String fileName) {
+		this(fileName, Letters.COMMA, Charset.forName("ISO-8859-1"));
+	}
+
+	/**
+	 * Creates a {@link com.csvreader.CsvWriter CsvWriter} object using a Writer
+	 * to write data to.
+	 * 
+	 * @param outputStream
+	 *            The stream to write the column delimited data to.
+	 * @param delimiter
+	 *            The character to use as the column delimiter.
+	 */
+	public CsvWriter(Writer outputStream, char delimiter) {
+		if (outputStream == null) {
+			throw new IllegalArgumentException("Parameter outputStream can not be null.");
+		}
+
+		this.outputStream = new PrintWriter(outputStream);
+		userSettings.Delimiter = delimiter;
+		initialized = true;
+	}
+
+	/**
+	 * Creates a {@link com.csvreader.CsvWriter CsvWriter} object using an
+	 * OutputStream to write data to.
+	 * 
+	 * @param outputStream
+	 *            The stream to write the column delimited data to.
+	 * @param delimiter
+	 *            The character to use as the column delimiter.
+	 * @param charset
+	 *            The {@link java.nio.charset.Charset Charset} to use while
+	 *            writing the data.
+	 */
+	public CsvWriter(OutputStream outputStream, char delimiter, Charset charset) {
+		this(new OutputStreamWriter(outputStream, charset), delimiter);
+	}
+
+	/**
+	 * Gets the character being used as the column delimiter.
+	 * 
+	 * @return The character being used as the column delimiter.
+	 */
+	public char getDelimiter() {
+		return userSettings.Delimiter;
+	}
+
+	/**
+	 * Sets the character to use as the column delimiter.
+	 * 
+	 * @param delimiter
+	 *            The character to use as the column delimiter.
+	 */
+	public void setDelimiter(char delimiter) {
+		userSettings.Delimiter = delimiter;
+	}
+
+	public char getRecordDelimiter() {
+		return userSettings.RecordDelimiter;
+	}
+
+	/**
+	 * Sets the character to use as the record delimiter.
+	 * 
+	 * @param recordDelimiter
+	 *            The character to use as the record delimiter. Default is
+	 *            combination of standard end of line characters for Windows,
+	 *            Unix, or Mac.
+	 */
+	public void setRecordDelimiter(char recordDelimiter) {
+		useCustomRecordDelimiter = true;
+		userSettings.RecordDelimiter = recordDelimiter;
+	}
+
+	/**
+	 * Gets the character to use as a text qualifier in the data.
+	 * 
+	 * @return The character to use as a text qualifier in the data.
+	 */
+	public char getTextQualifier() {
+		return userSettings.TextQualifier;
+	}
+
+	/**
+	 * Sets the character to use as a text qualifier in the data.
+	 * 
+	 * @param textQualifier
+	 *            The character to use as a text qualifier in the data.
+	 */
+	public void setTextQualifier(char textQualifier) {
+		userSettings.TextQualifier = textQualifier;
+	}
+
+	/**
+	 * Whether text qualifiers will be used while writing data or not.
+	 * 
+	 * @return Whether text qualifiers will be used while writing data or not.
+	 */
+	public boolean getUseTextQualifier() {
+		return userSettings.UseTextQualifier;
+	}
+
+	/**
+	 * Sets whether text qualifiers will be used while writing data or not.
+	 * 
+	 * @param useTextQualifier
+	 *            Whether to use a text qualifier while writing data or not.
+	 */
+	public void setUseTextQualifier(boolean useTextQualifier) {
+		userSettings.UseTextQualifier = useTextQualifier;
+	}
+
+	public int getEscapeMode() {
+		return userSettings.EscapeMode;
+	}
+
+	public void setEscapeMode(int escapeMode) {
+		userSettings.EscapeMode = escapeMode;
+	}
+
+	public void setComment(char comment) {
+		userSettings.Comment = comment;
+	}
+
+	public char getComment() {
+		return userSettings.Comment;
+	}
+
+	/**
+	 * Whether fields will be surrounded by the text qualifier even if the
+	 * qualifier is not necessarily needed to escape this field.
+	 * 
+	 * @return Whether fields will be forced to be qualified or not.
+	 */
+	public boolean getForceQualifier() {
+		return userSettings.ForceQualifier;
+	}
+
+	/**
+	 * Use this to force all fields to be surrounded by the text qualifier even
+	 * if the qualifier is not necessarily needed to escape this field. Default
+	 * is false.
+	 * 
+	 * @param forceQualifier
+	 *            Whether to force the fields to be qualified or not.
+	 */
+	public void setForceQualifier(boolean forceQualifier) {
+		userSettings.ForceQualifier = forceQualifier;
+	}
+
+	/**
+	 * Writes another column of data to this record.
+	 * 
+	 * @param content
+	 *            The data for the new column.
+	 * @param preserveSpaces
+	 *            Whether to preserve leading and trailing whitespace in this
+	 *            column of data.
+	 * @exception IOException
+	 *                Thrown if an error occurs while writing data to the
+	 *                destination stream.
+	 */
+	public void write(String content, boolean preserveSpaces)
+			throws IOException {
+		checkClosed();
+
+		checkInit();
+
+		if (content == null) {
+			content = "";
+		}
+
+		if (!firstColumn) {
+			outputStream.write(userSettings.Delimiter);
+		}
+
+		boolean textQualify = userSettings.ForceQualifier;
+
+		if (!preserveSpaces && content.length() > 0) {
+			content = content.trim();
+		}
+
+		if (!textQualify
+				&& userSettings.UseTextQualifier
+				&& (content.indexOf(userSettings.TextQualifier) > -1
+						|| content.indexOf(userSettings.Delimiter) > -1
+						|| (!useCustomRecordDelimiter && (content
+								.indexOf(Letters.LF) > -1 || content
+								.indexOf(Letters.CR) > -1))
+						|| (useCustomRecordDelimiter && content
+								.indexOf(userSettings.RecordDelimiter) > -1)
+						|| (firstColumn && content.length() > 0 && content
+								.charAt(0) == userSettings.Comment) ||
+				// check for empty first column, which if on its own line must
+				// be qualified or the line will be skipped
+				(firstColumn && content.length() == 0))) {
+			textQualify = true;
+		}
+
+		if (userSettings.UseTextQualifier && !textQualify
+				&& content.length() > 0 && preserveSpaces) {
+			char firstLetter = content.charAt(0);
+
+			if (firstLetter == Letters.SPACE || firstLetter == Letters.TAB) {
+				textQualify = true;
+			}
+
+			if (!textQualify && content.length() > 1) {
+				char lastLetter = content.charAt(content.length() - 1);
+
+				if (lastLetter == Letters.SPACE || lastLetter == Letters.TAB) {
+					textQualify = true;
+				}
+			}
+		}
+
+		if (textQualify) {
+			outputStream.write(userSettings.TextQualifier);
+
+			if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH) {
+				content = replace(content, "" + Letters.BACKSLASH, ""
+						+ Letters.BACKSLASH + Letters.BACKSLASH);
+				content = replace(content, "" + userSettings.TextQualifier, ""
+						+ Letters.BACKSLASH + userSettings.TextQualifier);
+			} else {
+				content = replace(content, "" + userSettings.TextQualifier, ""
+						+ userSettings.TextQualifier
+						+ userSettings.TextQualifier);
+			}
+		} else if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH) {
+			content = replace(content, "" + Letters.BACKSLASH, ""
+					+ Letters.BACKSLASH + Letters.BACKSLASH);
+			content = replace(content, "" + userSettings.Delimiter, ""
+					+ Letters.BACKSLASH + userSettings.Delimiter);
+
+			if (useCustomRecordDelimiter) {
+				content = replace(content, "" + userSettings.RecordDelimiter,
+						"" + Letters.BACKSLASH + userSettings.RecordDelimiter);
+			} else {
+				content = replace(content, "" + Letters.CR, ""
+						+ Letters.BACKSLASH + Letters.CR);
+				content = replace(content, "" + Letters.LF, ""
+						+ Letters.BACKSLASH + Letters.LF);
+			}
+
+			if (firstColumn && content.length() > 0
+					&& content.charAt(0) == userSettings.Comment) {
+				if (content.length() > 1) {
+					content = "" + Letters.BACKSLASH + userSettings.Comment
+							+ content.substring(1);
+				} else {
+					content = "" + Letters.BACKSLASH + userSettings.Comment;
+				}
+			}
+		}
+
+		outputStream.write(content);
+
+		if (textQualify) {
+			outputStream.write(userSettings.TextQualifier);
+		}
+
+		firstColumn = false;
+	}
+
+	/**
+	 * Writes another column of data to this record.&nbsp;Does not preserve
+	 * leading and trailing whitespace in this column of data.
+	 * 
+	 * @param content
+	 *            The data for the new column.
+	 * @exception IOException
+	 *                Thrown if an error occurs while writing data to the
+	 *                destination stream.
+	 */
+	public void write(String content) throws IOException {
+		write(content, false);
+	}
+
+	public void writeComment(String commentText) throws IOException {
+		checkClosed();
+
+		checkInit();
+
+		outputStream.write(userSettings.Comment);
+
+		outputStream.write(commentText);
+
+		if (useCustomRecordDelimiter) {
+			outputStream.write(userSettings.RecordDelimiter);
+		} else {
+			outputStream.println();
+		}
+
+		firstColumn = true;
+	}
+
+	/**
+	 * Writes a new record using the passed in array of values.
+	 * 
+	 * @param values
+	 *            Values to be written.
+	 * 
+	 * @param preserveSpaces
+	 *            Whether to preserver leading and trailing spaces in columns
+	 *            while writing out to the record or not.
+	 * 
+	 * @throws IOException
+	 *             Thrown if an error occurs while writing data to the
+	 *             destination stream.
+	 */
+	public void writeRecord(String[] values, boolean preserveSpaces)
+			throws IOException {
+		if (values != null && values.length > 0) {
+			for (int i = 0; i < values.length; i++) {
+				write(values[i], preserveSpaces);
+			}
+
+			endRecord();
+		}
+	}
+
+	/**
+	 * Writes a new record using the passed in array of values.
+	 * 
+	 * @param values
+	 *            Values to be written.
+	 * 
+	 * @throws IOException
+	 *             Thrown if an error occurs while writing data to the
+	 *             destination stream.
+	 */
+	public void writeRecord(String[] values) throws IOException {
+		writeRecord(values, false);
+	}
+
+	/**
+	 * Ends the current record by sending the record delimiter.
+	 * 
+	 * @exception IOException
+	 *                Thrown if an error occurs while writing data to the
+	 *                destination stream.
+	 */
+	public void endRecord() throws IOException {
+		checkClosed();
+
+		checkInit();
+
+		if (useCustomRecordDelimiter) {
+			outputStream.write(userSettings.RecordDelimiter);
+		} else {
+			outputStream.println();
+		}
+
+		firstColumn = true;
+	}
+
+	/**
+	 * 
+	 */
+	private void checkInit() throws IOException {
+		if (!initialized) {
+			if (fileName != null) {
+				outputStream = new PrintWriter(new OutputStreamWriter(
+						new FileOutputStream(fileName), charset));
+			}
+
+			initialized = true;
+		}
+	}
+
+	/**
+	 * Clears all buffers for the current writer and causes any buffered data to
+	 * be written to the underlying device.
+	 */
+	public void flush() {
+		outputStream.flush();
+	}
+
+	/**
+	 * Closes and releases all related resources.
+	 */
+	public void close() {
+		if (!closed) {
+			close(true);
+
+			closed = true;
+		}
+	}
+
+	/**
+	 * 
+	 */
+	private void close(boolean closing) {
+		if (!closed) {
+			if (closing) {
+				charset = null;
+			}
+
+			try {
+				if (initialized) {
+					outputStream.close();
+				}
+			} catch (Exception e) {
+				// just eat the exception
+			}
+
+			outputStream = null;
+
+			closed = true;
+		}
+	}
+
+	/**
+	 * 
+	 */
+	private void checkClosed() throws IOException {
+		if (closed) {
+			throw new IOException(
+			"This instance of the CsvWriter class has already been closed.");
+		}
+	}
+
+	/**
+	 * 
+	 */
+	protected void finalize() {
+		close(false);
+	}
+
+	private class Letters {
+		public static final char LF = '\n';
+
+		public static final char CR = '\r';
+
+		public static final char QUOTE = '"';
+
+		public static final char COMMA = ',';
+
+		public static final char SPACE = ' ';
+
+		public static final char TAB = '\t';
+
+		public static final char POUND = '#';
+
+		public static final char BACKSLASH = '\\';
+
+		public static final char NULL = '\0';
+	}
+
+	private class UserSettings {
+		// having these as publicly accessible members will prevent
+		// the overhead of the method call that exists on properties
+		public char TextQualifier;
+
+		public boolean UseTextQualifier;
+
+		public char Delimiter;
+
+		public char RecordDelimiter;
+
+		public char Comment;
+
+		public int EscapeMode;
+
+		public boolean ForceQualifier;
+
+		public UserSettings() {
+			TextQualifier = Letters.QUOTE;
+			UseTextQualifier = true;
+			Delimiter = Letters.COMMA;
+			RecordDelimiter = Letters.NULL;
+			Comment = Letters.POUND;
+			EscapeMode = ESCAPE_MODE_DOUBLED;
+			ForceQualifier = false;
+		}
+	}
+
+	public static String replace(String original, String pattern, String replace) {
+		final int len = pattern.length();
+		int found = original.indexOf(pattern);
+
+		if (found > -1) {
+			StringBuffer sb = new StringBuffer();
+			int start = 0;
+
+			while (found != -1) {
+				sb.append(original.substring(start, found));
+				sb.append(replace);
+				start = found + len;
+				found = original.indexOf(pattern, start);
+			}
+
+			sb.append(original.substring(start));
+
+			return sb.toString();
+		} else {
+			return original;
+		}
+	}
+}

+ 116 - 0
src/main/java/de/mcs/utils/ArrayUtils.java

@@ -0,0 +1,116 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2006 by MCS
+ * -------------------------------------- Created on 12.01.2006 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Implements some utils for array hanling.
+ * 
+ * @author w.klaas
+ * 
+ */
+public final class ArrayUtils {
+
+    /** prevent instancing. */
+    private ArrayUtils() {
+    }
+
+    /**
+     * checking if 2 arrays are equal up to len length.
+     * 
+     * @param id
+     *            first array
+     * @param id2
+     *            second array
+     * @param len
+     *            len to check.
+     * @return <code>true</code> if the array are equal, otherwise
+     *         <code>false</code>
+     */
+    public static boolean equals(final byte[] id, final byte[] id2,
+            final int len) {
+        if ((id != null) && (id2 != null)) {
+            if ((id.length >= len) && (id2.length >= len)) {
+                byte[] newid = new byte[len];
+                System.arraycopy(id, 0, newid, 0, len);
+                byte[] newid2 = new byte[len];
+                System.arraycopy(id2, 0, newid2, 0, len);
+                return Arrays.equals(newid, newid2);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * converting a Strign into an array. Format is the internal format of the
+     * toString methode of a array.
+     * 
+     * @param value
+     *            the string to convert.
+     * @return String[]
+     */
+    public static String[] stringToArray(final String value) {
+        String line = value.trim();
+        int pos = 0;
+        int colonCount = 0;
+        int start = 0;
+        int stop = line.length();
+        boolean inField = false;
+        ArrayList<String> list = new ArrayList<String>();
+        while (pos < line.length()) {
+            char chr = line.charAt(pos);
+            switch (chr) {
+            case '[':
+                if (!inField) {
+                    colonCount++;
+                    if (colonCount == 1) {
+                        start = pos + 1;
+                    }
+                }
+                break;
+            case ']':
+                if (!inField) {
+                    colonCount--;
+                    if (colonCount == 0) {
+                        stop = pos;
+                        list.add(line.substring(start, stop));
+                        start = pos + 1;
+                    }
+                }
+                break;
+            case ',':
+                if (!inField) {
+                    if (colonCount == 1) {
+                        stop = pos;
+                        list.add(line.substring(start, stop));
+                        start = pos + 1;
+                    }
+                }
+                break;
+            case '"':
+                inField = !inField;
+                break;
+            default:
+                break;
+            }
+            pos++;
+        }
+        return list.toArray(new String[0]);
+    }
+}

+ 48 - 0
src/main/java/de/mcs/utils/Checksum16.java

@@ -0,0 +1,48 @@
+package de.mcs.utils;
+
+/*
+ * Copyright (c) 2004 by MCS Software --------------------------------------
+ * Created on 04.01.2005 by w.klaas
+ */
+/**
+ * Build 16 bit checksum of some values.
+ * 
+ * @author w.klaas
+ */
+public final class Checksum16 {
+
+    /** private constructor to prevent instancing. */
+    private Checksum16() {
+    };
+
+    /**
+     * parses the string and build the 16 bit checksum.
+     * 
+     * @param str
+     *            string to build checksum from
+     * @return int 16 bit checksum
+     */
+    public static int parseString(final String str) {
+        return parseArray(str.getBytes());
+    }
+
+    /**
+     * parses the byte array and build the 16 bit checksum.
+     * 
+     * @param array
+     *            array to build checksum from
+     * @return int 16 bit checksum
+     */
+    public static int parseArray(final byte[] array) {
+        int lo = 0;
+        int hi = 0;
+        for (int n = 0; n < array.length; n++) {
+            if ((n % 2) == 0) {
+                lo = lo ^ array[n];
+            } else {
+                hi = hi ^ array[n];
+            }
+        }
+        return lo + (hi << 8);
+    }
+}

+ 73 - 0
src/main/java/de/mcs/utils/ConversionException.java

@@ -0,0 +1,73 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2006 by MCS
+ * -------------------------------------- Created on 13.01.2006 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+/**
+ * This exception will be thrown in cases where tsome error occured in the
+ * conversions class.
+ * 
+ * @author w.klaas
+ * 
+ */
+public class ConversionException extends Exception {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = -7326424522974928879L;
+
+    /**
+     * constructor.
+     * 
+     */
+    public ConversionException() {
+        super();
+    }
+
+    /**
+     * Constructor with message text.
+     * 
+     * @param message
+     *            the message
+     */
+    public ConversionException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor with message text and another exception.
+     * 
+     * @param message
+     *            the message
+     * @param cause
+     *            Throwable
+     */
+    public ConversionException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructor with another exception.
+     * 
+     * @param cause
+     *            Throwable
+     */
+    public ConversionException(final Throwable cause) {
+        super(cause);
+    }
+
+}

+ 624 - 0
src/main/java/de/mcs/utils/Conversions.java

@@ -0,0 +1,624 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2006 by MCS
+ * -------------------------------------- Created on 06.01.2006 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+/**
+ * This class encapsulates methods to deal with arrays of numbers, converting
+ * from numbers to bytes and bytes to numbers.
+ * <p>
+ * Methods xxxToByte() convert a Java array of primitive numbers (int, short,
+ * ...) to a Java array of bytes. Methods byteToXxx() convert from a Java array
+ * of bytes into a Java array of primitive numbers (int, short, ...)
+ * <p>
+ * Variant interfaces convert a section of an array, and also can convert to
+ * sub-classes of Java <b>Number</b>.
+ */
+public final class Conversions {
+
+  /** to prevent instancing. */
+  private Conversions() {
+
+  }
+
+  /**
+   * Convert an array of bytes into an array of ints.
+   * 
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of int
+   */
+  public static int[] byteToInt(final byte[] data) throws ConversionException {
+    if ((data.length % 4) > 0) {
+      throw new ConversionException("array length doesn't match.");
+    }
+    int[] values = new int[data.length >> 2];
+
+    int i = 0;
+    for (byte b : data) {
+      int pos = i >> 2;
+      values[pos] <<= 8;
+      int aByte = b < 0 ? b + 256 : b;
+      values[pos] |= aByte;
+      i++;
+    }
+    return values;
+  }
+
+  /**
+   * Convert an array of bytes into an array of floats.
+   * 
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of float
+   */
+  public static float[] byteToFloat(final byte[] data) throws ConversionException {
+    throw new ConversionException("not implemented yet.");
+  }
+
+  /**
+   * Convert an array of bytes into an array of shorts.
+   * 
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of short
+   */
+  public static short[] byteToShort(final byte[] data) throws ConversionException {
+    if ((data.length % 2) > 0) {
+      throw new ConversionException("array length doesn't match.");
+    }
+    short[] values = new short[data.length >> 1];
+
+    int i = 0;
+    for (byte b : data) {
+      int pos = i >> 1;
+      values[pos] <<= 8;
+      int aByte = b < 0 ? b + 256 : b;
+      values[pos] |= aByte;
+      i++;
+    }
+    return values;
+  }
+
+  /**
+   * Convert an array of bytes into an array of long.
+   * 
+   * @param data
+   *          The input array of bytes
+   * 
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of long
+   * 
+   */
+  public static long[] byteToLong(final byte[] data) throws ConversionException {
+    if ((data.length % 8) > 0) {
+      throw new ConversionException("array length doesn't match.");
+    }
+    long[] values = new long[data.length >> 3];
+
+    int i = 0;
+    for (byte b : data) {
+      int pos = i >> 3;
+      values[pos] <<= 8;
+      int aByte = b < 0 ? b + 256 : b;
+      values[pos] |= aByte;
+      i++;
+    }
+    return values;
+  }
+
+  /**
+   * Convert an array of bytes into an array of double.
+   * 
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of double
+   */
+  public static double[] byteToDouble(final byte[] data) throws ConversionException {
+    throw new ConversionException("not implemented yet.");
+  }
+
+  /**
+   * Convert a range from an array of bytes into an array of int.
+   * 
+   * @param start
+   *          The position in the input array of bytes to start
+   * @param len
+   *          The number of 'int' to convert
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of 'len' int
+   */
+  public static int[] byteToInt(final int start, final int len, final byte[] data) throws ConversionException {
+    byte[] datas = new byte[len];
+    System.arraycopy(data, start, datas, 0, len);
+    return byteToInt(datas);
+  }
+
+  /**
+   * Convert 4 bytes from an array of bytes into a single int.
+   * 
+   * @param start
+   *          The position in the input array of bytes to start
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return The integer value of the bytes.
+   */
+  public static int byteToInt(final byte[] data, final int start) throws ConversionException {
+    int[] ival = new int[1];
+    ival = byteToInt(start, 4, data);
+    return (ival[0]);
+  }
+
+  /**
+   * Convert a range from an array of bytes into an array of short.
+   * 
+   * @param start
+   *          The position in the input array of bytes to start
+   * @param len
+   *          The number of 'short' to convert
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of 'len' short
+   */
+  public static short[] byteToShort(final int start, final int len, final byte[] data) throws ConversionException {
+    byte[] datas = new byte[len];
+    System.arraycopy(data, start, datas, 0, len);
+    return byteToShort(datas);
+  }
+
+  /**
+   * Convert 2 bytes from an array of bytes into a single short.
+   * 
+   * @param start
+   *          The position in the input array of bytes to start
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return The short value of the bytes.
+   */
+  public static short byteToShort(final byte[] data, final int start) throws ConversionException {
+    short[] sval = new short[1];
+    sval = byteToShort(start, 2, data);
+    return (sval[0]);
+  }
+
+  /**
+   * Convert a range from an array of bytes into an array of float.
+   * 
+   * @param start
+   *          The position in the input array of bytes to start
+   * @param len
+   *          The number of 'float' to convert
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of 'len' float
+   */
+  public static float[] byteToFloat(final int start, final int len, final byte[] data) throws ConversionException {
+    byte[] datas = new byte[len];
+    System.arraycopy(data, start, datas, 0, len);
+    return byteToFloat(datas);
+  }
+
+  /**
+   * Convert 4 bytes from an array of bytes into a single float.
+   * 
+   * @param start
+   *          The position in the input array of bytes to start
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return The float value of the bytes.
+   */
+  public static float byteToFloat(final byte[] data, final int start) throws ConversionException {
+    float[] fval = new float[1];
+    fval = byteToFloat(start, 4, data);
+    return (fval[0]);
+  }
+
+  /**
+   * Convert a range from an array of bytes into an array of long.
+   * 
+   * @param start
+   *          The position in the input array of bytes to start
+   * @param len
+   *          The number of 'long' to convert
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of 'len' long
+   */
+  public static long[] byteToLong(final int start, final int len, final byte[] data) throws ConversionException {
+    byte[] datas = new byte[len];
+    System.arraycopy(data, start, datas, 0, len);
+    return byteToLong(datas);
+  }
+
+  /**
+   * Convert 8(?) bytes from an array of bytes into a single long.
+   * 
+   * @param start
+   *          The position in the input array of bytes to start
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return The long value of the bytes.
+   */
+  public static long byteToLong(final byte[] data, final int start) throws ConversionException {
+    long[] lval = new long[1];
+    lval = byteToLong(start, 8, data);
+    return (lval[0]);
+  }
+
+  /**
+   * Convert a range from an array of bytes into an array of double.
+   * 
+   * @param start
+   *          The position in the input array of bytes to start
+   * @param len
+   *          The number of 'double' to convert
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of 'len' double
+   */
+  public static double[] byteToDouble(final int start, final int len, final byte[] data) throws ConversionException {
+    byte[] datas = new byte[len];
+    System.arraycopy(data, start, datas, 0, len);
+    return byteToDouble(datas);
+  }
+
+  /**
+   * Convert 8 bytes from an array of bytes into a single double.
+   * 
+   * @param start
+   *          The position in the input array of bytes to start
+   * @param data
+   *          The input array of bytes
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return The double value of the bytes.
+   */
+  public static double byteToDouble(final byte[] data, final int start) throws ConversionException {
+    double[] dval = new double[1];
+    dval = byteToDouble(start, 8, data);
+    return (dval[0]);
+  }
+
+  /**
+   * Convert a range from an array of int into an array of bytes.
+   * 
+   * @param start
+   *          The position in the input array of int to start
+   * @param len
+   *          The number of 'int' to convert
+   * @param data
+   *          The input array of int
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of bytes
+   */
+  public static byte[] intToByte(final int start, final int len, final int[] data) throws ConversionException {
+    int[] values = new int[len];
+    byte[] result = new byte[len * 4];
+    System.arraycopy(data, start, values, 0, len);
+    for (int i = 0; i < values.length; i++) {
+      byte[] news = intToByte(values[i]);
+      System.arraycopy(news, 0, result, i * 4, news.length);
+    }
+    return result;
+  }
+
+  /**
+   * Convert a range from an array of short into an array of bytes.
+   * 
+   * @param start
+   *          The position in the input array of int to start
+   * @param len
+   *          The number of 'short' to convert
+   * @param data
+   *          The input array of short
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of bytes
+   */
+  public static byte[] shortToByte(final int start, final int len, final short[] data) throws ConversionException {
+    short[] values = new short[len];
+    byte[] result = new byte[len * 2];
+    System.arraycopy(data, start, values, 0, len);
+    for (int i = 0; i < values.length; i++) {
+      byte[] news = shortToByte(values[i]);
+      System.arraycopy(news, 0, result, i * 2, news.length);
+    }
+    return result;
+  }
+
+  /**
+   * Convert a range from an array of float into an array of bytes.
+   * 
+   * @param start
+   *          The position in the input array of int to start
+   * @param len
+   *          The number of 'float' to convert
+   * @param data
+   *          The input array of float
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of bytes
+   */
+  public static byte[] floatToByte(final int start, final int len, final float[] data) throws ConversionException {
+    throw new ConversionException("not implemented yet.");
+  }
+
+  /**
+   * Convert a range from an array of long into an array of bytes.
+   * 
+   * @param start
+   *          The position in the input array of int to start
+   * @param len
+   *          The number of 'long' to convert
+   * @param data
+   *          The input array of long
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of bytes
+   */
+  public static byte[] longToByte(final int start, final int len, final long[] data) throws ConversionException {
+    long[] values = new long[len];
+    byte[] result = new byte[len * 8];
+    System.arraycopy(data, start, values, 0, len);
+    for (int i = 0; i < values.length; i++) {
+      byte[] news = longToByte(values[i]);
+      System.arraycopy(news, 0, result, i * 8, news.length);
+    }
+    return result;
+  }
+
+  /**
+   * Convert a range from an array of double into an array of bytes.
+   * 
+   * @param start
+   *          The position in the input array of double to start
+   * @param len
+   *          The number of 'double' to convert
+   * @param data
+   *          The input array of double
+   * @throws ConversionException
+   *           if something goes wrong.
+   * @return an array of bytes
+   */
+  public static byte[] doubleToByte(final int start, final int len, final double[] data) throws ConversionException {
+    throw new ConversionException("not implemented yet.");
+  }
+
+  /**
+   * Convert a single byte into an array of one byte.
+   * <p>
+   * (This is a trivial method.)
+   * 
+   * @param data
+   *          The input byte
+   * @return an array of bytes
+   */
+  public static byte[] byteToByte(final byte data) {
+    return new byte[] { data };
+  }
+
+  /**
+   * Convert a single Byte object into an array of one byte.
+   * <p>
+   * (This is an almost trivial method.)
+   * 
+   * @param data
+   *          The input Byte
+   * @return an array of bytes
+   */
+  public static byte[] byteToByte(final Byte data) {
+    return byteToByte(data.byteValue());
+  }
+
+  /**
+   * Convert a single int into an array of 4 bytes.
+   * 
+   * @param data
+   *          The input int
+   * @return an array of bytes
+   */
+  public static byte[] intToByte(final int data) {
+    byte[] value = new byte[4];
+    value[0] = (byte) (data >> 24);
+    value[1] = (byte) (data >> 16);
+    value[2] = (byte) (data >> 8);
+    value[3] = (byte) (data);
+    return value;
+  }
+
+  /**
+   * Convert a single Integer object into an array of 4 bytes.
+   * 
+   * @param data
+   *          The input Integer
+   * @return an array of bytes
+   */
+  public static byte[] intToByte(final Integer data) {
+    return intToByte(data.intValue());
+  }
+
+  /**
+   * Convert a single short into an array of 2 bytes.
+   * 
+   * @param data
+   *          The input short
+   * @return an array of bytes
+   */
+  public static byte[] shortToByte(final short data) {
+    byte[] value = new byte[2];
+    value[0] = (byte) (data >> 8);
+    value[1] = (byte) (data);
+    return value;
+  }
+
+  /**
+   * Convert a single Short object into an array of 2 bytes.
+   * 
+   * @param data
+   *          The input Short
+   * @return an array of bytes
+   */
+  public static byte[] shortToByte(final Short data) {
+    return shortToByte(data.shortValue());
+  }
+
+  /**
+   * Convert a single float into an array of 4 bytes.
+   * 
+   * @param data
+   *          The input float
+   * @return an array of bytes
+   */
+  public static byte[] floatToByte(final float data) {
+    return null;
+  }
+
+  /**
+   * Convert a single Float object into an array of 4 bytes.
+   * 
+   * @param data
+   *          The input Float
+   * @return an array of bytes
+   */
+  public static byte[] floatToByte(final Float data) {
+    return floatToByte(data.floatValue());
+  };
+
+  /**
+   * Convert a single long into an array of 8 bytes.
+   * 
+   * @param data
+   *          The input long
+   * @return an array of bytes
+   */
+  public static byte[] longToByte(final long data) {
+    byte[] value = new byte[8];
+    value[0] = (byte) (data >> 56);
+    value[1] = (byte) (data >> 48);
+    value[2] = (byte) (data >> 40);
+    value[3] = (byte) (data >> 32);
+    value[4] = (byte) (data >> 24);
+    value[5] = (byte) (data >> 16);
+    value[6] = (byte) (data >> 8);
+    value[7] = (byte) (data);
+    return value;
+  }
+
+  /**
+   * Convert a single Long object into an array of 8(?) bytes.
+   * 
+   * @param data
+   *          The input Long
+   * @return an array of bytes
+   */
+  public static byte[] longToByte(final Long data) {
+    return longToByte(data.longValue());
+  }
+
+  /**
+   * Convert a single double into an array of 8 bytes.
+   * 
+   * @param data
+   *          The input double
+   * @return an array of bytes
+   */
+  public static byte[] doubleToByte(final double data) {
+    return null;
+  }
+
+  /**
+   * Convert a single Double object into an array of 8 bytes.
+   * 
+   * @param data
+   *          The input Double
+   * @return an array of bytes
+   */
+  public static byte[] doubleToByte(final Double data) {
+    return doubleToByte(data.doubleValue());
+  }
+
+  /**
+   * Create a Number object from an array of bytes.
+   * 
+   * @param barray
+   *          The bytes to be converted
+   * @param obj
+   *          Input object of the desired output class. Must be a sub-class of
+   *          Number.
+   * @return A Object of the type of obj.
+   * @throws ConversionException
+   *           if something goes wrong.
+   */
+  @SuppressWarnings("rawtypes")
+  public static Object byteToNumber(final byte[] barray, final Object obj) throws ConversionException {
+    Class theClass = obj.getClass();
+    String type = theClass.getName();
+    Object retobj = null;
+
+    if (type.equals("java.lang.Integer")) {
+      int[] i = Conversions.byteToInt(0, 1, barray);
+      retobj = new Integer(i[0]);
+    } else if (type.equals("java.lang.Byte")) {
+      retobj = new Byte(barray[0]);
+    } else if (type.equals("java.lang.Short")) {
+      short[] f = Conversions.byteToShort(0, 1, barray);
+      retobj = new Short(f[0]);
+    } else if (type.equals("java.lang.Float")) {
+      float[] f = Conversions.byteToFloat(0, 1, barray);
+      retobj = new Float(f[0]);
+    } else if (type.equals("java.lang.Long")) {
+      long[] f = Conversions.byteToLong(0, 1, barray);
+      retobj = new Long(f[0]);
+    } else if (type.equals("java.lang.Double")) {
+      double[] f = Conversions.byteToDouble(0, 1, barray);
+      retobj = new Double(f[0]);
+    } else {
+      /* exception: unsupported type */
+      ConversionException ex = new ConversionException("byteToNumber: setfield bad type: " + obj + " " + type);
+      throw (ex);
+    }
+    return (retobj);
+  }
+}

+ 64 - 0
src/main/java/de/mcs/utils/ExtendedBoolean.java

@@ -0,0 +1,64 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+/**
+ * this function is missing in java.
+ * 
+ * @author W.Klaas
+ */
+public final class ExtendedBoolean {
+    /**
+     * Damit man diese Klasse nicht instanzieren kann.
+     */
+    private ExtendedBoolean() {
+        // nothing to do here
+    }
+
+    /**
+     * String in Boolean verwandeln.
+     * 
+     * @param name
+     *            String
+     * @return boolean
+     */
+    public static boolean toBoolean(final String name) {
+        return ((name != null) && (name.equalsIgnoreCase("true") || name
+                .equalsIgnoreCase("1")));
+    }
+
+    /**
+     * String in Boolean verwandeln.
+     * 
+     * @param name
+     *            String
+     * @param defaultValue
+     *            the default value.
+     * @return boolean
+     */
+    public static boolean toBoolean(final String name,
+            final boolean defaultValue) {
+        boolean value = defaultValue;
+        if ((name != null)) {
+            if (!name.equals("")) {
+                value = name.equalsIgnoreCase("true")
+                        || name.equalsIgnoreCase("1");
+            }
+        }
+        return value;
+    }
+}

+ 218 - 0
src/main/java/de/mcs/utils/ExtendedProperties.java

@@ -0,0 +1,218 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2007 by MCS
+ * -------------------------------------- Created on 15.01.2007 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/**
+ * 
+ */
+package de.mcs.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * This class extands the normal properties class with functions to directly get
+ * simple types.
+ * 
+ * @author w.klaas
+ * 
+ */
+public class ExtendedProperties extends Properties {
+
+  /**
+  * 
+  */
+  private static final long serialVersionUID = -2429305070469193013L;
+
+  /**
+   * Constructor for directly loaing properties from file.
+   * 
+   * @param file
+   *          the file to load properties from.
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public ExtendedProperties(final File file) throws IOException {
+    super();
+    load(new FileInputStream(file));
+  }
+
+  /**
+   * Simple Constructor.
+   */
+  public ExtendedProperties() {
+    super();
+  }
+
+  /**
+   * directly getting boolean value.
+   * 
+   * @param key
+   *          the property to get.
+   * @return the desired boolean value.
+   */
+  public final boolean getBool(final String key) {
+    return Boolean.parseBoolean(getProperty(key));
+  }
+
+  /**
+   * directly getting boolean value.
+   * 
+   * @param key
+   *          the property to get.
+   * @param defaultValue
+   *          the default value to get, if the property is not present.
+   * @return the desired boolean value.
+   */
+  public final boolean getBool(final String key, final boolean defaultValue) {
+    return Boolean.parseBoolean(getProperty(key, Boolean.toString(defaultValue)));
+  }
+
+  /**
+   * directly getting integer value.
+   * 
+   * @param key
+   *          the property to get.
+   * @return the desired int value.
+   */
+  public final int getInt(final String key) {
+    return NumberHelper.parseInt(getProperty(key));
+  }
+
+  /**
+   * directly getting integer value.
+   * 
+   * @param key
+   *          the property to get.
+   * @param defaultValue
+   *          the default value to get, if the property is not present.
+   * @return the desired int value.
+   */
+  public final int getInt(final String key, final int defaultValue) {
+    return NumberHelper.parseInt(getProperty(key, Integer.toString(defaultValue)));
+  }
+
+  /**
+   * directly getting integer value.
+   * 
+   * @param key
+   *          the property to get.
+   * @param defaultValue
+   *          the default value to get, if the property is not present.
+   * @return the desired int value.
+   */
+  public final int getInt(final String key, final String defaultValue) {
+    return NumberHelper.parseInt(getProperty(key, defaultValue));
+  }
+
+  /**
+   * directly getting long value.
+   * 
+   * @param key
+   *          the property to get.
+   * @return the desired long value.
+   */
+  public final long getLong(final String key) {
+    return Long.parseLong(getProperty(key));
+  }
+
+  /**
+   * directly getting long value.
+   * 
+   * @param key
+   *          the property to get.
+   * @param defaultValue
+   *          the default value to get, if the property is not present.
+   * @return the desired long value.
+   */
+  public final long getLong(final String key, final long defaultValue) {
+    return Long.parseLong(getProperty(key, Long.toString(defaultValue)));
+  }
+
+  /**
+   * directly getting a file value.
+   * 
+   * @param key
+   *          the property to get.
+   * @return the desired file.
+   */
+  public final File getFile(final String key) {
+    if (containsKey(key)) {
+      return new File(getProperty(key));
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * directly getting a file value.
+   * 
+   * @param key
+   *          the property to get.
+   * @param defaultValue
+   *          the default value to get, if the property is not present.
+   * @return the desired file.
+   */
+  public final File getFile(final String key, final File defaultValue) {
+    return new File(getProperty(key, defaultValue.getAbsolutePath()));
+  }
+
+  /**
+   * directly getting a string array value.
+   * 
+   * @param key
+   *          the property to get.
+   * @return the desired string array.
+   */
+  public final String[] getStrings(final String key) {
+    String value = getProperty(key);
+    if (value != null) {
+      return StringUtils.csvStringToArray(value, ',', '"');
+    }
+    return null;
+  }
+
+  /**
+   * directly getting a string array value.
+   * 
+   * @param key
+   *          the property to get.
+   * @param defaultValue
+   *          the default value to get, if the property is not present.
+   * @return the desired string array.
+   */
+  public final String[] getStrings(final String key, final String[] defaultValue) {
+    String value = getProperty(key);
+    if (value != null) {
+      return StringUtils.csvStringToArray(value, ',', '"');
+    }
+    return defaultValue;
+  }
+
+  /**
+   * directly getting integer value.
+   * 
+   * @param key
+   *          the property to get.
+   * @param defaultValue
+   *          the default value to get, if the property is not present.
+   * @return the desired int value.
+   */
+  public final long getMSec(final String key, final String defaultValue) {
+    return NumberHelper.parseTime(getProperty(key, defaultValue));
+  }
+}

+ 701 - 0
src/main/java/de/mcs/utils/FileTool.java

@@ -0,0 +1,701 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.mcs.utils;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * filetool class.
+ * 
+ * @author w.klaas
+ * @deprecated please use the Files class.
+ */
+public final class FileTool {
+
+  /** prevent instancing. */
+  private FileTool() {
+  }
+
+  /**
+   * Retrieves the existance of a file or directory (to be or not to be....).
+   * 
+   * @param fileName
+   *          name of the file or directory.
+   * @return success
+   */
+  public static boolean fileExists(final String fileName) {
+    return new File(fileName).exists();
+  }
+
+  /**
+   * Create a directory; all ancestor directories must exist.
+   * 
+   * @param directory
+   *          name of the directory
+   * @return success
+   */
+  public static boolean createDirectory(final String directory) {
+    boolean success = (new File(directory)).mkdir();
+    if (!success) {
+      System.err.println("Directory [" + directory + "] creation failed !");
+    }
+    return success;
+  }
+
+  /**
+   * Create a directory; all non-existent ancestor directories are.
+   * automatically created
+   * 
+   * @param directory
+   *          the directory
+   * @return success
+   */
+  public static boolean createDirectories(final String directory) {
+    boolean success = (new File(directory)).mkdirs();
+    if (!success) {
+      System.err.println("Directory [" + directory + "] creation failed !");
+    }
+    return success;
+  }
+
+  /**
+   * Create a directory and a number of subdirectories specified in the String
+   * array automatically created.
+   * 
+   * @param aDirectory
+   *          the directory
+   * @param subdirectories
+   *          the subdirectories
+   * @return success
+   */
+  public static boolean createDirectoryStructure(final String aDirectory, final String[] subdirectories) {
+    String directory = aDirectory;
+    boolean success = true;
+    directory = eliminateDoubleSlashes(directory);
+    if (!fileExists(directory)) {
+      if (!createDirectories(directory)) {
+        return false;
+      }
+    }
+    if (!directory.endsWith("/")) {
+      directory += '/';
+    }
+    for (int iLoop = 0; iLoop < subdirectories.length; iLoop++) {
+      File subDir = new File(directory + subdirectories[iLoop]);
+      if (!subDir.exists()) {
+        if (!createDirectory(directory + subdirectories[iLoop])) {
+          success = false;
+          break;
+        }
+      }
+      // success=(iLoop==subdirectories.length-1);
+    }
+
+    if (!success) {
+      System.err.println("Directory [" + directory + "] creation failed !");
+    }
+    return success;
+  }
+
+  /**
+   * Move / Rename source location to destination. String array automatically
+   * created
+   * 
+   * @param source
+   *          source
+   * @param destination
+   *          destination
+   * @return success
+   */
+  public static boolean move(final String source, final String destination) {
+    try {
+      return (new File(source)).renameTo(new File(destination));
+    } catch (SecurityException e) {
+      System.err.println("The file " + source + " could not moved to " + destination + " : " + e.getLocalizedMessage());
+      return false;
+    }
+  }
+
+  /**
+   * Copy source location to destination.
+   * 
+   * @param source
+   *          source
+   * @param destination
+   *          destination
+   * @return success
+   */
+  public static boolean copy(final String source, final String destination) {
+    searchDepth = 0;
+    return filecopy(source, destination, true);
+  }
+
+  /**
+   * Copy source location to destination.
+   * 
+   * @param source
+   *          source
+   * @param destination
+   *          destination
+   * @param subdirectories
+   *          subdirectories
+   * @return success
+   */
+  public static boolean copy(final String source, final String destination, final boolean subdirectories) {
+    searchDepth = 0;
+    return filecopy(source, destination, subdirectories);
+  }
+
+  /**
+   * Filename filter class.
+   * 
+   * @author w.klaas
+   */
+  static class FileIncludes implements FilenameFilter {
+
+    /** directory to eveluate. */
+    private File dir;
+
+    /** the regex. */
+    private Pattern mask;
+
+    /** prevent instancing with the default constructor. */
+    // private FileIncludes() {
+    // throw new UnsupportedOperationException();
+    // }
+
+    /**
+     * is the mask a mask?
+     * 
+     * @param mask
+     *          the mask
+     * 
+     * @return <code>true</code> if the mask containing a * or ?
+     */
+    private static boolean isMask(final String mask) {
+      int pos = mask.indexOf('*');
+      if (pos < 0) {
+        pos = mask.indexOf('?');
+      }
+      return pos >= 0;
+    }
+
+    /**
+     * special regexp chars : '.', '\\', '$', '^', '(' , ')', '[', ']' ,'+', .
+     * (must be masked if in mask) special mask chars to convert for regexp :
+     * '*' -> '.*', '?' -> '.+'
+     */
+    private static String regexpSpecChars = ".\\$^()[]+";
+
+    /**
+     * convert the mask to a regualr expression.
+     * 
+     * @param aMask
+     *          the mask
+     * @return String regular expression.
+     */
+    String toRegExp(final String aMask) {
+      StringBuffer sb = new StringBuffer();
+      int length = aMask.length();
+      for (int index = 0; index < length; index++) {
+        char ch = aMask.charAt(index);
+        if (ch == '*') {
+          sb.append(".*");
+        } else if (ch == '?') {
+          sb.append(".+");
+        } else if (regexpSpecChars.indexOf(ch) >= 0) {
+          sb.append('\\').append(ch);
+        } else {
+          sb.append(ch);
+        }
+      }
+      return sb.toString();
+    }
+
+    /**
+     * Constructs a FilenameFilter instance to filter files, which base name
+     * (without the directoy path) that match the given regular expression.
+     * 
+     * @param aDir
+     *          the directory
+     * @param aMask
+     *          the mask
+     * @throws java.util.regex.PatternSyntaxException
+     */
+    public FileIncludes(final File aDir, final String aMask) {
+      this.dir = aDir;
+      this.mask = Pattern.compile(toRegExp(aMask));
+      // System.out.println("FileIncludes: dir=" + dir.toString() + ",
+      // mask=" + mask);
+
+    }
+
+    /**
+     * Tests if a specified file should be included in a file list.
+     * 
+     * @param aDir
+     *          the directory in which the file was found.
+     * @param name
+     *          the name of the file.
+     * @return <code>true</code> if and only if the name should be included in
+     *         the file list; <code>false</code> otherwise.
+     * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
+     */
+    public boolean accept(final File aDir, final String name) {
+      // System.out.println("accept: dir=" + dir.toString() + ", name=" +
+      // name);
+      if (aDir.equals(this.dir)) {
+        boolean matches = mask.matcher(name).matches();
+        // System.out.println("accept: result=" + matches);
+        return matches;
+      } else {
+        // System.out.println("accept: result=" + false);
+        return false;
+      }
+    }
+
+  }
+
+  /** default subdirectory depth to copy. */
+  private static int searchDepth = 0;
+
+  /**
+   * Copy source location to destination.
+   * 
+   * @param source
+   *          The pathname of a single directory or a single file or multiple
+   *          files through a filemask.
+   * @param destination
+   *          destination path
+   * @param subdirectories
+   *          allow subdirectories to be copied
+   * @return <code>true</code> if this operation succseeded
+   */
+  protected static boolean filecopy(final String source, final String destination, final boolean subdirectories) {
+
+    boolean success = true; // just be optimistic
+    File sourceFile = new File(source);
+    if (sourceFile.isDirectory()) {
+      if (searchDepth++ > 0 && !subdirectories) {
+        return true;
+      }
+      if (!fileExists(destination)) {
+        createDirectories(destination);
+      }
+      String[] subFiles = sourceFile.list();
+      for (int iLoop = 0; iLoop < subFiles.length; iLoop++) {
+        filecopy(source + File.separator + subFiles[iLoop], destination + File.separator + subFiles[iLoop],
+            subdirectories);
+      }
+    } else {
+      // create destination directory if it does not exist
+      int fSlash = destination.lastIndexOf("/"), bSlash = destination.lastIndexOf("\\");
+      int slash;
+      if (fSlash > bSlash) {
+        slash = fSlash;
+      } else {
+        slash = bSlash;
+      }
+      if (slash > -1) {
+        String path = destination.substring(0, slash);
+        File dest = new File(path);
+        if (!dest.exists()) {
+          createDirectories(path);
+        }
+      }
+
+      File[] inputFiles = new File[] { sourceFile };
+      String mask = sourceFile.getName();
+      if (FileIncludes.isMask(mask)) {
+        File sourceDir = sourceFile.getParentFile();
+        if (null != sourceDir && mask.length() > 0) {
+          try {
+            FilenameFilter filter = new FileIncludes(sourceDir, mask);
+            inputFiles = sourceDir.listFiles(filter);
+          } catch (java.util.regex.PatternSyntaxException e) {
+            // nothing to do here
+          }
+        }
+      }
+
+      File destFile = new File(destination);
+      if (null != inputFiles) {
+        if (destFile.isFile() && inputFiles.length > 1) {
+          System.err.println("For copying multiple files to the destination \"" + destination
+              + "\" it must be a directory and NOT A SINGLE file.");
+          success = false;
+        } else {
+          for (int idx = 0; idx < inputFiles.length; idx++) {
+            File inputFile = inputFiles[idx];
+
+            File destinationFile;
+            if (destFile.isDirectory()) {
+              destinationFile = new File(destFile, inputFile.getName());
+            } else {
+              destinationFile = destFile;
+            }
+
+            try {
+              // calling filecopy using FileChannel
+              Files.fileCopy(inputFile, destinationFile, true);
+              success = true;
+            } catch (FileNotFoundException eFNF) {
+              System.err.println("The source file could not be found. " + eFNF);
+              System.err.println("Tried to copy " + inputFile + " to " + destinationFile + " : " + eFNF);
+              success = false;
+              break;
+            } catch (IOException eIO) {
+              System.err.println("IOException occured while copying file.");
+              System.err.println("Tried to copy " + inputFile + " to " + destinationFile + " : " + eIO);
+              success = false;
+              break;
+            }
+          }
+        }
+      }
+    }
+    return success;
+  }
+
+  /**
+   * @param filename
+   *          A filename that may be a relative filename like "." or "..".
+   * @return The absolute <code>File</code> name instance fo the given
+   *         (relative) filename. Return <code>null</code> if ".." is given and
+   *         the current or parent directory is /
+   */
+  public static File getAbsoluteFile(final String filename) {
+    File file = new File(filename).getAbsoluteFile();
+    if (".".equals(file.getName())) {
+      file = file.getParentFile();
+    } else if ("..".equals(file.getName())) {
+      file = file.getParentFile();
+      if (null != file) {
+        file = file.getParentFile();
+      }
+    }
+    return file;
+  }
+
+  /**
+   * making a string to a directory with ending on File.separator.
+   * 
+   * @param aPath
+   *          path to convert
+   * @return string
+   */
+  public static String makeDirectory(final String aPath) {
+    String path = aPath;
+    String separatorChar = System.getProperty("file.separator");
+    if (path != null && !path.endsWith(separatorChar)) {
+      path += separatorChar;
+    }
+    return path;
+  }
+
+  /**
+   * removes a directory recursively. All Files and Subdirectories are removed.
+   * If bRecursive is false and the directory is not empty, an IOException is
+   * thrown.
+   * 
+   * @param filename
+   *          path or file to remove
+   * @param bRecursive
+   *          delete all subfolders too
+   * @return success <code>true</code> if all files could be removed, else
+   *         return <code>false</code>
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static boolean remove(final String filename, final boolean bRecursive) throws IOException {
+    return remove(filename, bRecursive, null);
+  }
+
+  /**
+   * removes a directory recursively. All Files and Subdirectories are removed.
+   * If bRecursive is false and the directory is not empty, an IOException is
+   * thrown.
+   * 
+   * @param filename
+   *          path or file to remove
+   * @param bRecursive
+   *          delete all subfolders too
+   * @param excludes
+   *          list with files to exclude from deleting
+   * @return success <code>true</code> if all files could be removed, else
+   *         return <code>false</code>
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static boolean remove(final String filename, final boolean bRecursive, final List<String> excludes)
+      throws IOException {
+    boolean bSuccess = true;
+    if (filename != null && 0 != filename.length()) {
+      File file = new File(filename);
+      if (file.isDirectory()) {
+        String[] files = file.list();
+
+        if (!bRecursive && files.length > 0) {
+          System.err.println();
+          throw new IOException("Directory " + file.getAbsolutePath() + " is not Empty");
+        } else {
+          for (int iLoop = 0; iLoop < files.length; iLoop++) {
+            String toRemove = FileTool.convertSlashes(file.getAbsolutePath() + "/" + files[iLoop]);
+            if (!isExcluded(toRemove, excludes)) {
+              if (!remove(toRemove, true)) {
+                bSuccess = false;
+              }
+            }
+          }
+          if (bSuccess) {
+            if (!isExcluded(filename, excludes)) {
+              bSuccess = remove(filename);
+            }
+          }
+        }
+      } else {
+        if (!isExcluded(filename, excludes)) {
+          bSuccess = remove(filename);
+        }
+      }
+    }
+    return bSuccess;
+  }
+
+  /**
+   * checks if a file is in the exclude list.
+   * 
+   * @param filename
+   *          file to test
+   * @param excludes
+   *          list with the excluded files
+   * @return boolean <code>true</code> if the file is in the excludelist, else
+   *         <code>false</code>
+   */
+  private static boolean isExcluded(final String filename, final List<String> excludes) {
+    boolean bDelete = true;
+    if (excludes != null && excludes.size() > 0) {
+      for (int iFileLoop = 0; iFileLoop < excludes.size(); iFileLoop++) {
+        String exclude = FileTool.convertSlashes((String) excludes.get(iFileLoop));
+        if (filename.indexOf(exclude) > -1 || exclude.startsWith(filename)) {
+          bDelete = false;
+          break;
+        }
+      }
+    }
+    return !bDelete;
+  }
+
+  /**
+   * Delete the file.
+   * 
+   * @param filename
+   *          file to delete
+   * @return <code>true</code> if the file could be delete, <code>false</code>
+   *         if not.
+   */
+  public static boolean remove(final String filename) {
+    try {
+      return new File(filename).delete();
+    } catch (SecurityException e) {
+      System.err.println("The file " + filename + " could not be removed: " + e.getLocalizedMessage());
+      return false;
+    }
+  }
+
+  /**
+   * reading a file into an arraylist. Every line is one entry.
+   * 
+   * @param fileName
+   *          file to read
+   * @return ArrayList with all lines
+   */
+  public static List<String> readFileToList(final String fileName) {
+    ArrayList<String> list = new ArrayList<String>();
+    BufferedReader br = null;
+    try {
+      br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
+    } catch (FileNotFoundException e) {
+      System.err.println("The File " + fileName + " could not be found !!");
+      e.printStackTrace();
+      System.exit(3);
+    }
+    String line = "";
+    try {
+      while ((line = br.readLine()) != null) {
+        if (line.trim().length() > 0 && line.trim().charAt(0) != '#') {
+          list.add(line);
+        }
+      }
+    } catch (IOException e1) {
+      System.err.println("IOException occured while reading " + fileName + "");
+      e1.printStackTrace();
+    } finally {
+      try {
+        br.close();
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+    return list;
+  }
+
+  /**
+   * reading a file into an arraylist. Every line is one entry.
+   * 
+   * @param fileName
+   *          file to read
+   * @return ArrayList with all lines
+   */
+  public static String readFileToString(final String fileName) {
+    StringBuffer buffer = new StringBuffer();
+    BufferedReader br = null;
+    try {
+      br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
+    } catch (FileNotFoundException e) {
+      System.err.println("The File " + fileName + " could not be found !!");
+      e.printStackTrace();
+      System.exit(3);
+    }
+    String line = "";
+    try {
+      while ((line = br.readLine()) != null) {
+        if (line.trim().length() > 0 && line.trim().charAt(0) != '#') {
+          buffer.append(line);
+        }
+      }
+
+    } catch (IOException e1) {
+      System.err.println("IOException occured while reading " + fileName + "");
+      e1.printStackTrace();
+    } finally {
+      try {
+        br.close();
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+
+    return buffer.toString();
+  }
+
+  /**
+   * simple writing a string into a file.
+   * 
+   * @param filename
+   *          of the file to create
+   * @param content
+   *          string to write into the file.
+   */
+  public static void writeStringToFile(final String filename, final String content) {
+    File file = new File(filename);
+    try {
+      BufferedWriter bufWriter = new BufferedWriter(new FileWriter(file));
+      bufWriter.write(content);
+      bufWriter.close();
+    } catch (IOException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Eleminates // in a filename.
+   * 
+   * @param filename
+   *          to convert
+   * @return String
+   */
+  public static String eliminateDoubleSlashes(final String filename) {
+    String result = "";
+    if (filename.indexOf(":") == -1 && filename.startsWith("//")) {
+      result = filename.substring(0, 2) + filename.substring(2).replaceAll("//", "/");
+    } else {
+      result = filename.replaceAll("//", "/");
+    }
+    return result;
+  }
+
+  /**
+   * converts 2 backslashes into one slash.
+   * 
+   * @param path
+   *          to convert
+   * @return string
+   */
+  public static String convertSlashes(final String path) {
+    return path.replaceAll("\\\\", "/");
+  }
+
+  /**
+   * converts one slash into 2 backslashes.
+   * 
+   * @param path
+   *          to convert
+   * @return string
+   */
+  public static String convertBackSlashes(final String path) {
+    return path.replaceAll("/", "\\\\");
+  }
+
+  /**
+   * converts only if backslash is not proceeded by a blank.
+   * 
+   * @param aPath
+   *          to convert
+   * @return string
+   */
+  public static String convertBackSlashesNoParams(final String aPath) {
+    String path = aPath;
+    int slash = -1;
+    while ((slash = path.indexOf("/", slash + 1)) > -1) {
+      if (slash > -1 && path.charAt(slash - 1) != ' ') {
+        String sLineStart = path.substring(0, slash);
+        String sLineEnd = path.substring(slash + 1);
+        path = sLineStart + "\\" + sLineEnd;
+      }
+    }
+    return path;
+  }
+
+  /**
+   * converting the given path with an File.separator at the end.
+   * 
+   * @param aPath
+   *          the path to convert
+   * @return the converted path
+   */
+  public static String normPath(final String aPath) {
+    if (!aPath.endsWith("/") && !aPath.endsWith("\\")) {
+      return aPath + File.separator;
+    } else {
+      return aPath;
+    }
+  }
+}

+ 112 - 0
src/main/java/de/mcs/utils/FileZipper.java

@@ -0,0 +1,112 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * FolderZiper provide a static method to zip a folder.
+ * 
+ * @author pitchoun
+ */
+public final class FileZipper {
+
+  /** prevent instancing. */
+  private FileZipper() {
+  }
+
+  /**
+   * Zip the srcFolder into the destFileZipFile. All the folder subtree of the
+   * src folder is added to the destZipFile archive. TODO handle the usecase of
+   * srcFolder being en file.
+   * 
+   * @param srcFile
+   *          String, the path of the srcFile
+   * @param destZipFile
+   *          String, the path of the destination zipFile. This file will be
+   *          created or erased.
+   */
+  public static void zipFile(final String srcFile, final String destZipFile) {
+    ZipOutputStream zip = null;
+    FileOutputStream fileWriter = null;
+    try {
+      File file = new File(destZipFile);
+      if (!file.getParentFile().exists()) {
+        file.getParentFile().mkdirs();
+      }
+      fileWriter = new FileOutputStream(destZipFile);
+      zip = new ZipOutputStream(fileWriter);
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+
+    addToZip("", new File(srcFile).getAbsolutePath(), zip);
+
+    try {
+      zip.flush();
+      zip.close();
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+
+  /**
+   * Write the content of srcFile in a new ZipEntry, named path+srcFile, of the
+   * zip stream. The result is that the srcFile will be in the path folder in
+   * the generated archive.
+   * 
+   * @param path
+   *          String, the relatif path with the root archive.
+   * @param srcName
+   *          String, the absolute path of the file to add
+   * @param zip
+   *          ZipOutputStram, the stream to use to write the given file.
+   */
+  private static void addToZip(final String path, final String srcName, final ZipOutputStream zip) {
+
+    File folder = new File(srcName);
+    if (!folder.isDirectory()) {
+      // Transfer bytes from in to out
+      byte[] buf = new byte[1024];
+      int len;
+      try {
+        File srcFile = new File(srcName);
+        FileInputStream in = new FileInputStream(srcName);
+        try {
+          String zipEntryPrefix = "";
+          if (!path.equals("")) {
+            zipEntryPrefix = path + "/";
+          }
+          ZipEntry zipEntry = new ZipEntry(zipEntryPrefix + folder.getName());
+          zipEntry.setTime(srcFile.lastModified());
+          zip.putNextEntry(zipEntry);
+          while ((len = in.read(buf)) > 0) {
+            zip.write(buf, 0, len);
+          }
+        } finally {
+          in.close();
+        }
+      } catch (Exception ex) {
+        ex.printStackTrace();
+      }
+    }
+  }
+}

+ 1381 - 0
src/main/java/de/mcs/utils/Files.java

@@ -0,0 +1,1381 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.nio.channels.FileChannel;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Die Klasse Files definiert verschiedene statische Hilfsmethoden.
+ * 
+ * @author w.klaas
+ */
+public final class Files {
+  private static final String SHA_ALGRORYTHM = "SHA-512";
+
+  /**
+   * Damit man diese Klasse nicht instanzieren kann.
+   */
+  private Files() {
+    // nothing to do here
+  }
+
+  /**
+   * Diese Methode dient der Erzeugung einer tempor�ren Datei in einem
+   * definierten Verzeichnis.
+   * 
+   * @param pre
+   *          Prefix der Datei (unter WIN nur 3 Buchstaben m�glich)
+   * @param suf
+   *          Suffix der Datei (Dateiendung)
+   * @param dir
+   *          Verzeichniss in dem die Datei erzeugt werden soll
+   * @return Verzeichnis und Dateiname der erzeugten Datei
+   * @throws IOException
+   *           wenn mal was schief geht
+   */
+  public static String getTempFileName(final String pre, final String suf, final String dir) throws IOException {
+    String filename;
+
+    File f = File.createTempFile(pre, suf, new File(dir));
+
+    filename = f.getAbsolutePath();
+
+    return filename;
+  }
+
+  /**
+   * @return String getting the actual temp path
+   */
+  public static String getTempPath() {
+    String strTempPath = null;
+    strTempPath = System.getProperty("java.io.tmpdir");
+    if (!strTempPath.endsWith(File.separator)) {
+      return strTempPath + File.separator;
+    } else {
+      return strTempPath;
+    }
+  }
+
+  public static File createTempDirectory(File parentPath) throws IOException {
+    int count = 0;
+    File folder = null;
+    do {
+      String foldername = new DecimalFormat("0000").format(count);
+      folder = new File(parentPath, foldername);
+      count++;
+    } while (folder.exists());
+
+    if (!(folder.mkdir())) {
+      throw new IOException("Could not create temp directory: " + folder.getAbsolutePath());
+    }
+
+    return (folder);
+  }
+
+  /**
+   * Ersetzung von ung�ltigen zeichen f�r den Blobnamen.
+   * 
+   * @param filename
+   *          Eingangsname
+   * @return String
+   */
+  public static String formatFileNameToBlobName(final String filename) {
+    String sFilename = filename;
+    sFilename = sFilename.replaceAll("[^0-9a-zA-Z]", "_");
+    sFilename = sFilename.replace('.', '_');
+    return sFilename;
+  }
+
+  /**
+   * Von der (vorhandenen) Datei den MD5 berechnen.
+   * 
+   * @param file
+   *          Dateiname
+   * @return MD5 String
+   */
+  @Deprecated
+  public static String computeMD5FromFile(final String file) {
+    return computeMD5FromFile(new File(file));
+  }
+
+  public static byte[] computeMD5BytesFromFile(final File file) {
+    InputStream in = null;
+    MessageDigest digest = null;
+    // Stream oeffnen
+    try {
+      in = new BufferedInputStream(new FileInputStream(file));
+
+      // MessageDigest-Objekt fuer MD5 erzeugen
+      try {
+        digest = MessageDigest.getInstance("MD5");
+
+        // Puffer
+        byte[] array = new byte[8 * 4096];
+
+        int length = 0;
+
+        // Blockweise lesen und Pruefsumme auffrischen
+        try {
+          while (length >= 0) {
+            length = in.read(array);
+            if (length > 0) {
+              digest.update(array, 0, length);
+            }
+          }
+          in.close();
+
+          // Pruefsumme berechnen
+          return digest.digest();
+        } catch (IOException e) {
+          System.err.println("error reading " + file + "(" + e.getMessage() + ")");
+        }
+      } catch (NoSuchAlgorithmException e) {
+        System.err.println("md5 not implemented");
+      }
+    } catch (FileNotFoundException e) {
+      System.err.println("File " + file + " not found");
+    }
+    return null;
+  }
+
+  public static byte[] computeSHABytesFromByteArray(final byte[] data) {
+    try {
+      MessageDigest digest = MessageDigest.getInstance(SHA_ALGRORYTHM);
+
+      // Blockweise lesen und Pruefsumme auffrischen
+      digest.update(data);
+
+      // Pruefsumme berechnen
+      return digest.digest();
+    } catch (NoSuchAlgorithmException e) {
+      System.err.println("md5 not implemented");
+    }
+    return null;
+  }
+
+  public static byte[] computeSHABytesFromFile(final File file) {
+    InputStream in = null;
+    MessageDigest digest = null;
+    // Stream oeffnen
+    try {
+      in = new BufferedInputStream(new FileInputStream(file));
+
+      // MessageDigest-Objekt fuer SHA256 erzeugen
+      try {
+        digest = MessageDigest.getInstance(SHA_ALGRORYTHM);
+
+        // Puffer
+        byte[] array = new byte[8 * 4096];
+
+        int length = 0;
+
+        // Blockweise lesen und Pruefsumme auffrischen
+        try {
+          while (length >= 0) {
+            length = in.read(array);
+            if (length > 0) {
+              digest.update(array, 0, length);
+            }
+          }
+          in.close();
+
+          // Pruefsumme berechnen
+          return digest.digest();
+        } catch (IOException e) {
+          System.err.println("error reading " + file + "(" + e.getMessage() + ")");
+        }
+      } catch (NoSuchAlgorithmException e) {
+        System.err.println("md5 not implemented");
+      }
+    } catch (FileNotFoundException e) {
+      System.err.println("File " + file + " not found");
+    }
+    return null;
+  }
+
+  /**
+   * Von der (vorhandenen) Datei den MD5 berechnen.
+   * 
+   * @param file
+   *          Dateiname
+   * @return MD5 String
+   */
+  public static String computeMD5FromFile(final File file) {
+    String s;
+    String sMD5 = "";
+    byte[] md5 = computeMD5BytesFromFile(file);
+    // Pruefsumme als Hex-Zahl ausgeben
+    for (int l = 0; l < md5.length; l++) {
+      // System.out.println(md5[l]);
+      int i;
+      if (md5[l] >= 0) {
+        i = md5[l];
+      } else {
+        i = 256 + md5[l];
+      }
+      s = Integer.toHexString(i);
+      while (s.length() < 2) {
+        s = "0" + s;
+      }
+      sMD5 += s;
+    }
+    return sMD5;
+  }
+
+  /**
+   * Von der (vorhandenen) Datei den MD5 berechnen.
+   * 
+   * @param file
+   *          Dateiname
+   * @return MD5 String
+   */
+  public static String computeSHAFromFile(final File file) {
+    byte[] digest = computeSHABytesFromFile(file);
+    return StringFormat.toHexString(digest);
+  }
+
+  /**
+   * A File copy which should be quite fast. If the destination file does not
+   * exist, it is created.
+   * 
+   * @param src
+   *          Source file
+   * @param destIn
+   *          Destination file
+   * @param keepDate
+   *          If <code>true</code> the destination obtains the soruce file
+   *          modification date (like a real file copy does). If If
+   *          <code>true</code> the destination will have the date at the time
+   *          is is copied.
+   * @throws IOException
+   *           sonst was
+   */
+  public static void fileCopy(final File src, final File destIn, final boolean keepDate) throws IOException {
+    File dest = destIn;
+    if (destIn.isDirectory()) {
+      dest = new File(destIn, src.getName());
+    }
+    if (!dest.exists()) {
+      dest.createNewFile();
+    }
+    FileInputStream fin = null;
+    FileOutputStream fout = null;
+    FileChannel fchIn = null;
+    FileChannel fchOut = null;
+
+    try {
+      fin = new FileInputStream(src);
+      fout = new FileOutputStream(dest);
+      fchIn = fin.getChannel();
+      fchOut = fout.getChannel();
+
+      long position = 0;
+      long transferred;
+
+      long remaining = fchIn.size();
+      // it seems, that transferTo "may or may not transfer all of the
+      // requested bytes",
+      // so do a loop.
+
+      fchOut.position(0);
+      fchIn.position(0);
+
+      while (remaining > 0) {
+        // transferred = fchIn.transferTo(position, remaining, fchOut);
+        transferred = fchOut.transferFrom(fchIn, position, remaining);
+        remaining -= transferred;
+        position += transferred;
+      }
+    } finally {
+      if (null != fchOut) {
+        fchOut.close();
+      }
+      if (null != fchIn) {
+        fchIn.close();
+      }
+      if (null != fout) {
+        fout.close();
+      }
+      if (null != fin) {
+        fin.close();
+      }
+    }
+    if (keepDate) {
+      dest.setLastModified(src.lastModified());
+    }
+  }
+
+  /**
+   * A File copy which should be quite fast. If the destination file does not
+   * exist, it is created.
+   * 
+   * @param src
+   *          Source file
+   * @param dest
+   *          Destination file
+   * @throws IOException
+   *           geht nicht
+   */
+  public static void fileCopy(final File src, final File dest) throws IOException {
+
+    fileCopy(src, dest, false);
+
+  }
+
+  /**
+   * getting all files recursivly from a directory.
+   * 
+   * @param baseDir
+   *          where to search from.
+   * @param recursive
+   *          search the file structure recursivly.
+   * @return File[] list of all files
+   */
+  public static File[] getFiles(final File baseDir, final boolean recursive) {
+    return getFiles(baseDir, recursive, false);
+  }
+
+  public static File[] getFiles(final File baseDir, final boolean recursive, final boolean excludeDotFiles) {
+    ArrayList<File> fileList = new ArrayList<File>();
+    File[] files = baseDir.listFiles();
+    if (files != null) {
+      for (int i = 0; i < files.length; i++) {
+        File file = files[i];
+        if (file.isDirectory()) {
+          if (!excludeDotFiles || !file.getName().startsWith(".")) {
+            File[] newFiles = getFiles(file, recursive, excludeDotFiles);
+            for (int j = 0; j < newFiles.length; j++) {
+              fileList.add(newFiles[j]);
+            }
+          }
+        } else if (file.isFile()) {
+          if (!excludeDotFiles || !file.getName().startsWith(".")) {
+            fileList.add(file);
+          }
+        }
+      }
+    }
+    return (File[]) fileList.toArray(new File[0]);
+  }
+
+  /**
+   * copy a list of list into a directory.
+   * 
+   * @param files
+   *          list with all files to copy.
+   * @param installSiteDir
+   *          where to copy to.
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public static void fileCopy(final File[] files, final File installSiteDir) throws IOException {
+    for (int i = 0; i < files.length; i++) {
+      File srcFile = files[i];
+      File destFile = new File(installSiteDir, srcFile.getName());
+      fileCopy(srcFile, destFile);
+    }
+  }
+
+  // copied from FileTool class.
+  /**
+   * Retrieves the existance of a file or directory (to be or not to be....).
+   * 
+   * @param fileName
+   *          name of the file or directory.
+   * @return success
+   */
+  public static boolean fileExists(final String fileName) {
+    return new File(fileName).exists();
+  }
+
+  /**
+   * Create a directory; all ancestor directories must exist.
+   * 
+   * @param directory
+   *          name of the directory
+   * @return success
+   */
+  public static boolean createDirectory(final String directory) {
+    boolean success = (new File(directory)).mkdir();
+    if (!success) {
+      System.err.println("Directory [" + directory + "] creation failed !");
+    }
+    return success;
+  }
+
+  /**
+   * Create a directory; all non-existent ancestor directories are.
+   * automatically created
+   * 
+   * @param directory
+   *          the directory to create.
+   * @return success
+   */
+  public static boolean createDirectories(final String directory) {
+    boolean success = (new File(directory)).mkdirs();
+    if (!success) {
+      System.err.println("Directory [" + directory + "] creation failed !");
+    }
+    return success;
+  }
+
+  /**
+   * Create a directory and a number of subdirectories specified in the String
+   * array automatically created.
+   * 
+   * @param aDirectory
+   *          the directory to create.
+   * @param subdirectories
+   *          array with the subdirectories to create.
+   * @return success
+   */
+  public static boolean createDirectoryStructure(final String aDirectory, final String[] subdirectories) {
+    String directory = aDirectory;
+    boolean success = true;
+    directory = eliminateDoubleSlashes(directory);
+    if (!fileExists(directory)) {
+      if (!createDirectories(directory)) {
+        return false;
+      }
+    }
+    if (!directory.endsWith("/")) {
+      directory += '/';
+    }
+    for (int iLoop = 0; iLoop < subdirectories.length; iLoop++) {
+      File subDir = new File(directory + subdirectories[iLoop]);
+      if (!subDir.exists()) {
+        if (!createDirectory(directory + subdirectories[iLoop])) {
+          success = false;
+          break;
+        }
+      }
+      // success=(iLoop==subdirectories.length-1);
+    }
+
+    if (!success) {
+      System.err.println("Directory [" + directory + "] creation failed !");
+    }
+    return success;
+  }
+
+  /**
+   * Move / Rename source location to destination. String array automatically
+   * created
+   * 
+   * @param source
+   *          source file
+   * @param destination
+   *          destination file
+   * @return success
+   * 
+   */
+  @Deprecated
+  public static boolean move(final String source, final String destination) {
+    try {
+      return (new File(source)).renameTo(new File(destination));
+    } catch (SecurityException e) {
+      System.err.println("The file " + source + " could not moved to " + destination + " : " + e.getLocalizedMessage());
+      return false;
+    }
+  }
+
+  /**
+   * Move / Rename source location to destination. String array automatically
+   * created
+   * 
+   * @param source
+   *          source file
+   * @param destination
+   *          destination file
+   * @return success
+   */
+  public static boolean move(final File source, final File destination) {
+    try {
+      return (source.renameTo(destination));
+    } catch (SecurityException e) {
+      System.err.println("The file " + source + " could not moved to " + destination + " : " + e.getLocalizedMessage());
+      return false;
+    }
+  }
+
+  /**
+   * Copy source location to destination.
+   * 
+   * @param source
+   *          source file
+   * @param destination
+   *          destination file
+   * @return success
+   */
+  @Deprecated
+  public static boolean copy(final String source, final String destination) {
+    searchDepth = 0;
+    return filecopy(source, destination, true);
+  }
+
+  /**
+   * Copy source location to destination.
+   * 
+   * @param source
+   *          source file
+   * @param destination
+   *          destination file
+   * @param subdirectories
+   *          <code>true</code> if subdirectories should be copied too,
+   *          otherwise <code>false</code>
+   * @return success
+   */
+  @Deprecated
+  public static boolean copy(final String source, final String destination, final boolean subdirectories) {
+    searchDepth = 0;
+    return filecopy(source, destination, subdirectories);
+  }
+
+  /**
+   * Filename filter class. This class will determine files with dos wildcards.
+   * 
+   * @author w.klaas
+   */
+  static class FileIncludes implements FilenameFilter {
+
+    /** directory to eveluate. */
+    private File dir;
+
+    /** the regex. */
+    private Pattern mask;
+
+    /**
+     * is the mask a mask?
+     * 
+     * @param mask
+     *          the mask
+     * @return <code>true</code> if the mask containing a * or ?
+     */
+    private static boolean isMask(final String mask) {
+      int pos = mask.indexOf('*');
+      if (pos < 0) {
+        pos = mask.indexOf('?');
+      }
+      return pos >= 0;
+    }
+
+    /**
+     * special regexp chars : '.', '\\', '$', '^', '(' , ')', '[', ']' ,'+', .
+     * (must be masked if in mask) special mask chars to convert for regexp :
+     * '*' -> '.*', '?' -> '.+'
+     */
+    private static String regexpSpecChars = ".\\$^()[]+";
+
+    /**
+     * convert the mask to a regualr expression.
+     * 
+     * @param aMask
+     *          the mask
+     * @return String regular expression.
+     */
+    String toRegExp(final String aMask) {
+      StringBuffer sb = new StringBuffer();
+      int length = aMask.length();
+      for (int index = 0; index < length; index++) {
+        char ch = aMask.charAt(index);
+        if (ch == '*') {
+          sb.append(".*");
+        } else if (ch == '?') {
+          sb.append(".+");
+        } else if (regexpSpecChars.indexOf(ch) >= 0) {
+          sb.append('\\').append(ch);
+        } else {
+          sb.append(ch);
+        }
+      }
+      return sb.toString();
+    }
+
+    /**
+     * Constructs a FilenameFilter instance to filter files, which base name
+     * (without the directoy path) that match the given regular expression.
+     * 
+     * @param aDir
+     *          the directory
+     * @param aMask
+     *          the mask
+     * @throws java.util.regex.PatternSyntaxException
+     */
+    public FileIncludes(final File aDir, final String aMask) {
+      this.dir = aDir;
+      this.mask = Pattern.compile(toRegExp(aMask));
+      // System.out.println("FileIncludes: dir=" + dir.toString() + ",
+      // mask=" + mask);
+
+    }
+
+    /**
+     * Tests if a specified file should be included in a file list.
+     * 
+     * @param aDir
+     *          the directory in which the file was found.
+     * @param name
+     *          the name of the file.
+     * @return <code>true</code> if and only if the name should be included in
+     *         the file list; <code>false</code> otherwise.
+     * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
+     */
+    public boolean accept(final File aDir, final String name) {
+      // System.out.println("accept: dir=" + dir.toString() + ", name=" +
+      // name);
+      if (aDir.equals(this.dir)) {
+        boolean matches = mask.matcher(name).matches();
+        // System.out.println("accept: result=" + matches);
+        return matches;
+      } else {
+        // System.out.println("accept: result=" + false);
+        return false;
+      }
+    }
+
+  }
+
+  /** default subdirectory depth to copy. */
+  private static int searchDepth = 0;
+
+  /**
+   * Copy source location to destination.
+   * 
+   * @param source
+   *          The pathname of a single directory or a single file or multiple
+   *          files through a filemask.
+   * @param destination
+   *          destination path
+   * @param subdirectories
+   *          allow subdirectories to be copied
+   * @return <code>true</code> if this operation succseeded
+   */
+  protected static boolean filecopy(final String source, final String destination, final boolean subdirectories) {
+
+    boolean success = true; // just be optimistic
+    File sourceFile = new File(source);
+    if (sourceFile.isDirectory()) {
+      if (searchDepth++ > 0 && !subdirectories) {
+        return true;
+      }
+      if (!fileExists(destination)) {
+        createDirectories(destination);
+      }
+      String[] subFiles = sourceFile.list();
+      for (int iLoop = 0; iLoop < subFiles.length; iLoop++) {
+        filecopy(source + File.separator + subFiles[iLoop], destination + File.separator + subFiles[iLoop],
+            subdirectories);
+      }
+    } else {
+      // create destination directory if it does not exist
+      int fSlash = destination.lastIndexOf("/"), bSlash = destination.lastIndexOf("\\");
+      int slash;
+      if (fSlash > bSlash) {
+        slash = fSlash;
+      } else {
+        slash = bSlash;
+      }
+      if (slash > -1) {
+        String path = destination.substring(0, slash);
+        File dest = new File(path);
+        if (!dest.exists()) {
+          createDirectories(path);
+        }
+      }
+
+      File[] inputFiles = new File[] { sourceFile };
+      String mask = sourceFile.getName();
+      if (FileIncludes.isMask(mask)) {
+        File sourceDir = sourceFile.getParentFile();
+        if (null != sourceDir && mask.length() > 0) {
+          try {
+            FilenameFilter filter = new FileIncludes(sourceDir, mask);
+            inputFiles = sourceDir.listFiles(filter);
+          } catch (java.util.regex.PatternSyntaxException e) {
+            // nothing to do here
+          }
+        }
+      }
+
+      File destFile = new File(destination);
+      if (null != inputFiles) {
+        if (destFile.isFile() && inputFiles.length > 1) {
+          System.err.println("For copying multiple files to the destination \"" + destination
+              + "\" it must be a directory and NOT A SINGLE file.");
+          success = false;
+        } else {
+          for (int idx = 0; idx < inputFiles.length; idx++) {
+            File inputFile = inputFiles[idx];
+
+            File destinationFile;
+            if (destFile.isDirectory()) {
+              destinationFile = new File(destFile, inputFile.getName());
+            } else {
+              destinationFile = destFile;
+            }
+
+            try {
+              // calling filecopy using FileChannel
+              Files.fileCopy(inputFile, destinationFile, true);
+              success = true;
+            } catch (FileNotFoundException eFNF) {
+              System.err.println("The source file could not be found. " + eFNF);
+              System.err.println("Tried to copy " + inputFile + " to " + destinationFile + " : " + eFNF);
+              success = false;
+              break;
+            } catch (IOException eIO) {
+              System.err.println("IOException occured while copying file.");
+              System.err.println("Tried to copy " + inputFile + " to " + destinationFile + " : " + eIO);
+              success = false;
+              break;
+            }
+          }
+        }
+      }
+    }
+    return success;
+  }
+
+  /**
+   * @param filename
+   *          A filename that may be a relative filename like "." or "..".
+   * @return The absolute <code>File</code> name instance fo the given
+   *         (relative) filename. Return <code>null</code> if ".." is given and
+   *         the current or parent directory is /
+   */
+  public static File getAbsoluteFile(final String filename) {
+    File file = new File(filename).getAbsoluteFile();
+    if (".".equals(file.getName())) {
+      file = file.getParentFile();
+    } else if ("..".equals(file.getName())) {
+      file = file.getParentFile();
+      if (null != file) {
+        file = file.getParentFile();
+      }
+    }
+    return file;
+  }
+
+  /**
+   * making a string to a directory with ending on File.separator.
+   * 
+   * @param aPath
+   *          path to convert
+   * @return string
+   */
+  public static String makeDirectory(final String aPath) {
+    String path = aPath;
+    String separatorChar = System.getProperty("file.separator");
+    if (path != null && !path.endsWith(separatorChar)) {
+      path += separatorChar;
+    }
+    return path;
+  }
+
+  /**
+   * removes a directory recursively. All Files and Subdirectories are removed.
+   * If bRecursive is false and the directory is not empty, an IOException is
+   * thrown.
+   * 
+   * @param filename
+   *          path or file to remove
+   * @param bRecursive
+   *          delete all subfolders too
+   * @return success <code>true</code> if all files could be removed, else
+   *         return <code>false</code>
+   * @throws IOException
+   *           if something goes wrong
+   */
+  @Deprecated
+  public static boolean remove(final String filename, final boolean bRecursive) throws IOException {
+    return remove(filename, bRecursive, null);
+  }
+
+  /**
+   * removes a directory recursively. All Files and Subdirectories are removed.
+   * If bRecursive is false and the directory is not empty, an IOException is
+   * thrown.
+   * 
+   * @param file
+   *          path or file to remove
+   * @param bRecursive
+   *          delete all subfolders too
+   * @return success <code>true</code> if all files could be removed, else
+   *         return <code>false</code>
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static boolean remove(final File file, final boolean bRecursive) throws IOException {
+    return remove(file, bRecursive, null);
+  }
+
+  /**
+   * removes a directory recursively. All Files and Subdirectories are removed.
+   * If bRecursive is false and the directory is not empty, an IOException is
+   * thrown.
+   * 
+   * @param filename
+   *          path or file to remove
+   * @param bRecursive
+   *          delete all subfolders too
+   * @param excludes
+   *          list with files to exclude from deleting
+   * @return success <code>true</code> if all files could be removed, else
+   *         return <code>false</code>
+   * @throws IOException
+   *           if something goes wrong
+   */
+  @Deprecated
+  public static boolean remove(final String filename, final boolean bRecursive, final List<String> excludes)
+      throws IOException {
+    boolean bSuccess = true;
+    if (filename != null && 0 != filename.length()) {
+      File file = new File(filename);
+      if (file.isDirectory()) {
+        String[] files = file.list();
+
+        if (!bRecursive && files.length > 0) {
+          System.err.println();
+          throw new IOException("Directory " + file.getAbsolutePath() + " is not Empty");
+        } else {
+          for (int iLoop = 0; iLoop < files.length; iLoop++) {
+            String toRemove = convertSlashes(file.getAbsolutePath() + "/" + files[iLoop]);
+            if (!isExcluded(toRemove, excludes)) {
+              if (!remove(toRemove, true)) {
+                bSuccess = false;
+              }
+            }
+          }
+          if (bSuccess) {
+            if (!isExcluded(filename, excludes)) {
+              bSuccess = remove(filename);
+            }
+          }
+        }
+      } else {
+        if (!isExcluded(filename, excludes)) {
+          bSuccess = remove(filename);
+        }
+      }
+    }
+    return bSuccess;
+  }
+
+  /**
+   * removes a directory recursively. All Files and Subdirectories are removed.
+   * If bRecursive is false and the directory is not empty, an IOException is
+   * thrown.
+   * 
+   * @param source
+   *          path or file to remove
+   * @param bRecursive
+   *          delete all subfolders too
+   * @param excludes
+   *          list with files to exclude from deleting
+   * @return success <code>true</code> if all files could be removed, else
+   *         return <code>false</code>
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static boolean remove(final File source, final boolean bRecursive, final List<String> excludes)
+      throws IOException {
+    boolean bSuccess = true;
+    if ((source != null) && source.exists()) {
+      if (source.isDirectory()) {
+        File[] files = source.listFiles();
+
+        if (!bRecursive && files.length > 0) {
+          System.err.println();
+          throw new IOException("Directory " + source.getAbsolutePath() + " is not Empty");
+        } else {
+          for (int iLoop = 0; iLoop < files.length; iLoop++) {
+            File file = files[iLoop];
+            if (!isExcluded(file, excludes)) {
+              if (!remove(file, true)) {
+                bSuccess = false;
+              }
+            }
+          }
+          if (bSuccess) {
+            if (!isExcluded(source, excludes)) {
+              bSuccess = source.delete();
+            }
+          }
+        }
+      } else {
+        if (!isExcluded(source, excludes)) {
+          bSuccess = source.delete();
+        }
+      }
+    }
+    return bSuccess;
+  }
+
+  /**
+   * checks if a file is in the exclude list.
+   * 
+   * @param filename
+   *          file to test
+   * @param excludes
+   *          list with the excluded files
+   * @return boolean <code>true</code> if the file is in the excludelist, else
+   *         <code>false</code>
+   */
+  @Deprecated
+  private static boolean isExcluded(final String filename, final List<String> excludes) {
+    boolean bDelete = true;
+    if (excludes != null && excludes.size() > 0) {
+      for (int iFileLoop = 0; iFileLoop < excludes.size(); iFileLoop++) {
+        String exclude = convertSlashes((String) excludes.get(iFileLoop));
+        if (filename.indexOf(exclude) > -1 || exclude.startsWith(filename)) {
+          bDelete = false;
+          break;
+        }
+      }
+    }
+    return !bDelete;
+  }
+
+  /**
+   * checks if a file is in the exclude list.
+   * 
+   * @param filename
+   *          file to test
+   * @param excludes
+   *          list with the excluded files
+   * @return boolean <code>true</code> if the file is in the excludelist, else
+   *         <code>false</code>
+   */
+  private static boolean isExcluded(final File file, final List<String> excludes) {
+    boolean bDelete = true;
+    if (excludes != null && excludes.size() > 0) {
+      for (int i = 0; i < excludes.size(); i++) {
+        String exclude = convertSlashes((String) excludes.get(i));
+        try {
+          if (file.getCanonicalPath().indexOf(exclude) > -1 || exclude.startsWith(file.getCanonicalPath())) {
+            bDelete = false;
+            break;
+          }
+        } catch (IOException e) {
+          bDelete = false;
+        }
+      }
+    }
+    return !bDelete;
+  }
+
+  /**
+   * Delete the file.
+   * 
+   * @param filename
+   *          file to delete
+   * @return <code>true</code> if the file could be delete, <code>false</code>
+   *         if not.
+   */
+  @Deprecated
+  public static boolean remove(final String filename) {
+    try {
+      return new File(filename).delete();
+    } catch (SecurityException e) {
+      System.err.println("The file " + filename + " could not be removed: " + e.getLocalizedMessage());
+      return false;
+    }
+  }
+
+  public static List<String> readFileToList(final String fileName) throws IOException {
+    return readFileToList(new File(fileName));
+  }
+
+  /**
+   * reading a file into an arraylist. Every line is one entry.
+   * 
+   * @param file
+   *          file to read
+   * @return ArrayList with all lines
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static List<String> readFileToList(final File file) throws IOException {
+    return readFileToList(file, true);
+  }
+
+  public static List<String> readFileToList(final File file, boolean comments) throws IOException {
+    ArrayList<String> list = new ArrayList<String>();
+    BufferedReader br = null;
+    br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
+    String line = "";
+    try {
+      while ((line = br.readLine()) != null) {
+        if (line.trim().length() > 0 && ((line.trim().charAt(0) != '#') || !comments)) {
+          list.add(line);
+        }
+      }
+    } finally {
+      try {
+        br.close();
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+    return list;
+  }
+
+  @Deprecated
+  public static void writeListToFile(final String fileName, List<String> list) throws IOException {
+    writeListToFile(new File(fileName), list);
+  }
+
+  /**
+   * reading a file into an arraylist. Every line is one entry.
+   * 
+   * @param file
+   *          file to read
+   * @param list
+   *          list of string to write into the file
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static void writeListToFile(final File file, List<String> list) throws IOException {
+    PrintWriter br = null;
+    br = new PrintWriter(new BufferedWriter(new FileWriter(file)));
+    for (Object entry : list) {
+      br.println(entry.toString());
+    }
+    br.close();
+  }
+
+  /**
+   * reading a file into an string.
+   * 
+   * @param fileName
+   *          file to read
+   * @return String with the content
+   */
+  @Deprecated
+  public static String readFileToString(final String fileName) {
+    StringBuilder buffer = new StringBuilder();
+    BufferedReader br = null;
+    try {
+      br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
+    } catch (FileNotFoundException e) {
+      System.err.println("The File " + fileName + " could not be found !!");
+      e.printStackTrace();
+      System.exit(3);
+    }
+    String line = "";
+    try {
+      while ((line = br.readLine()) != null) {
+        if (line.trim().length() > 0) {
+          buffer.append(line);
+          buffer.append("\r\n");
+        }
+      }
+    } catch (IOException e1) {
+      System.err.println("IOException occured while reading " + fileName + "");
+      e1.printStackTrace();
+    } finally {
+      try {
+        br.close();
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+    return buffer.toString();
+  }
+
+  /**
+   * reading a file into an string. checking and converting the lineendings.
+   * 
+   * @param file
+   *          file to read
+   * @return String with the content
+   */
+  public static String readFileToString(final File file) {
+    StringBuilder buffer = new StringBuilder();
+    BufferedReader br = null;
+    try {
+      br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
+    } catch (FileNotFoundException e) {
+      System.err.println("The File " + file + " could not be found !!");
+      e.printStackTrace();
+      System.exit(3);
+    }
+    String line = "";
+    try {
+      while ((line = br.readLine()) != null) {
+        if (line.trim().length() > 0) {
+          buffer.append(line);
+          buffer.append("\r\n");
+        }
+      }
+    } catch (IOException e1) {
+      System.err.println("IOException occured while reading " + file + "");
+      e1.printStackTrace();
+    } finally {
+      try {
+        br.close();
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+    return buffer.toString();
+  }
+
+  /**
+   * reading a file into an string. no checking and converting.
+   * 
+   * @param file
+   *          file to read
+   * @return String with the content
+   * @throws IOException
+   *           if something goes wrnog
+   */
+  public static String readFile(final File file) throws IOException {
+    BufferedInputStream br = null;
+    br = new BufferedInputStream(new FileInputStream(file));
+
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    StreamHelper.copyStream(br, out);
+    br.close();
+
+    return new String(out.toByteArray());
+  }
+
+  /**
+   * simple writing a string into a file.
+   * 
+   * @param filename
+   *          of the file to create
+   * @param content
+   *          string to write into the file.
+   */
+  @Deprecated
+  public static void writeStringToFile(final String filename, final String content) {
+    File file = new File(filename);
+    try {
+      writeStringToFile(file, content);
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * simple writing a string into a file.
+   * 
+   * @param file
+   *          of the file to create
+   * @param content
+   *          string to write into the file.
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static void writeStringToFile(final File file, final String content) throws IOException {
+    BufferedWriter bufWriter = new BufferedWriter(new FileWriter(file));
+    bufWriter.write(content);
+    bufWriter.close();
+  }
+
+  /**
+   * Eleminates // in a filename.
+   * 
+   * @param filename
+   *          to convert
+   * @return String
+   */
+  public static String eliminateDoubleSlashes(final String filename) {
+    String result = "";
+    if (filename.indexOf(":") == -1 && filename.startsWith("//")) {
+      result = filename.substring(0, 2) + filename.substring(2).replaceAll("//", "/");
+    } else {
+      result = filename.replaceAll("//", "/");
+    }
+    return result;
+  }
+
+  /**
+   * converts 2 backslashes into one slash.
+   * 
+   * @param path
+   *          to convert
+   * @return string
+   */
+  public static String convertSlashes(final String path) {
+    return path.replaceAll("\\\\", "/");
+  }
+
+  /**
+   * converts one slash into 2 backslashes.
+   * 
+   * @param path
+   *          to convert
+   * @return string
+   */
+  public static String convertBackSlashes(final String path) {
+    return path.replaceAll("/", "\\\\");
+  }
+
+  /**
+   * converts only if backslash is not proceeded by a blank.
+   * 
+   * @param aPath
+   *          to convert
+   * @return string
+   */
+  public static String convertBackSlashesNoParams(final String aPath) {
+    String path = aPath;
+    int slash = -1;
+    while ((slash = path.indexOf("/", slash + 1)) > -1) {
+      if (slash > -1 && path.charAt(slash - 1) != ' ') {
+        String sLineStart = path.substring(0, slash);
+        String sLineEnd = path.substring(slash + 1);
+        path = sLineStart + "\\" + sLineEnd;
+      }
+    }
+    return path;
+  }
+
+  /**
+   * converting the given path with an File.separator at the end.
+   * 
+   * @param aPath
+   *          the path to convert
+   * @return the converted path
+   */
+  public static String normPath(final String aPath) {
+    if (!aPath.endsWith("/") && !aPath.endsWith("\\")) {
+      return aPath + File.separator;
+    } else {
+      return aPath;
+    }
+  }
+
+  /**
+   * getting the file extension.
+   * 
+   * @param file
+   *          the file to get the extension from.
+   * @return the extension with "." or <code>null</code> if the file is not a
+   *         file.
+   */
+  public static String getExtension(final File file) {
+    if (file.isFile()) {
+      String name = file.getName();
+      if (name.indexOf((int) '.') >= 0) {
+        return name.substring(name.lastIndexOf("."));
+      } else {
+        return "";
+      }
+    }
+    return null;
+  }
+
+  /**
+   * getting the file extension.
+   * 
+   * @param name
+   *          the file to get the extension from.
+   * @return the extension with "." or <code>null</code> if the file is not a
+   *         file.
+   */
+  public static String getExtension(final String name) {
+    return name.substring(name.lastIndexOf("."));
+  }
+
+  /**
+   * getting the file name without extension.
+   * 
+   * @param file
+   *          the file to get the extension from.
+   * @return the name without "." or <code>null</code> if the file is not a
+   *         file.
+   */
+  public static String extractName(final File file) {
+    if (file.isFile()) {
+      String name = file.getName();
+      if (name.lastIndexOf(".") >= 0) {
+        return name.substring(0, name.lastIndexOf("."));
+      } else {
+        return name;
+      }
+    } else {
+      String name = file.getName();
+      if (name.indexOf(".") >= 0) {
+        return name.substring(0, name.lastIndexOf("."));
+      } else {
+        return name;
+      }
+    }
+  }
+
+  /**
+   * changing the file extension.
+   * 
+   * @param file
+   *          the file th change the extension
+   * @param newExtension
+   *          the new extension
+   * @return changed file name
+   */
+  public static File changeExtension(final File file, final String newExtension) {
+    return new File(file.getParentFile(), extractName(file) + newExtension);
+  }
+
+  public static String readableFileSize(long size) {
+    if (size <= 0)
+      return "0";
+    final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" };
+    int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
+    return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
+  }
+
+  public static String getDriveLetter(File file) throws IOException {
+    String canonicalPath = file.getCanonicalPath();
+    canonicalPath = canonicalPath.substring(0, 2);
+    return canonicalPath;
+  }
+
+  public static File getAppData() {
+    String appDataStr = System.getenv("APPDATA");
+    if ((appDataStr == null) || appDataStr.equals("")) {
+      appDataStr = System.getenv("LOCALAPPDATA");
+      if ((appDataStr == null) || appDataStr.equals("")) {
+        appDataStr = Files.getTempPath();
+      }
+    }
+    return new File(appDataStr);
+  }
+}

+ 135 - 0
src/main/java/de/mcs/utils/FolderZipper.java

@@ -0,0 +1,135 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * FolderZiper provide a static method to zip a folder.
+ * 
+ * @author pitchoun
+ */
+public final class FolderZipper {
+
+  /** prevent instancing. */
+  private FolderZipper() {
+  }
+
+  /**
+   * Zip the srcFolder into the destFileZipFile. All the folder subtree of the
+   * src folder is added to the destZipFile archive. TODO handle the usecase of
+   * srcFolder being en file.
+   * 
+   * @param srcFolder
+   *          String, the path of the srcFolder
+   * @param destZipFile
+   *          String, the path of the destination zipFile. This file will be
+   *          created or erased.
+   */
+  public static void zipFolder(final String srcFolder, final String destZipFile) {
+    ZipOutputStream zip = null;
+    FileOutputStream fileWriter = null;
+    try {
+      File file = new File(destZipFile);
+      if (!file.getParentFile().exists()) {
+        file.getParentFile().mkdirs();
+      }
+      fileWriter = new FileOutputStream(destZipFile);
+      zip = new ZipOutputStream(fileWriter);
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+
+    addFolderToZip("", srcFolder, zip);
+    try {
+      zip.flush();
+      zip.close();
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+
+  /**
+   * Write the content of srcFile in a new ZipEntry, named path+srcFile, of the
+   * zip stream. The result is that the srcFile will be in the path folder in
+   * the generated archive.
+   * 
+   * @param path
+   *          String, the relatif path with the root archive.
+   * @param srcName
+   *          String, the absolute path of the file to add
+   * @param zip
+   *          ZipOutputStram, the stream to use to write the given file.
+   */
+  private static void addToZip(final String path, final String srcName, final ZipOutputStream zip) {
+
+    File folder = new File(srcName);
+    if (folder.isDirectory()) {
+      if (path.equals("")) {
+        addFolderToZip(folder.getName(), srcName, zip);
+      } else {
+        addFolderToZip(path + "/" + folder.getName(), srcName, zip);
+      }
+    } else {
+      // Transfer bytes from in to out
+      byte[] buf = new byte[1024];
+      int len;
+      try {
+        File srcFile = new File(srcName);
+        FileInputStream in = new FileInputStream(srcName);
+        try {
+          String zipEntryPrefix = "";
+          if (!path.equals("")) {
+            zipEntryPrefix = path + "/";
+          }
+          ZipEntry zipEntry = new ZipEntry(zipEntryPrefix + folder.getName());
+          zipEntry.setTime(srcFile.lastModified());
+          zip.putNextEntry(zipEntry);
+          while ((len = in.read(buf)) > 0) {
+            zip.write(buf, 0, len);
+          }
+        } finally {
+          in.close();
+        }
+      } catch (Exception ex) {
+        ex.printStackTrace();
+      }
+    }
+  }
+
+  /**
+   * add the srcFolder to the zip stream.
+   * 
+   * @param path
+   *          String, the relatif path with the root archive.
+   * @param srcFolder
+   *          String, the absolute path of the file to add
+   * @param zip
+   *          ZipOutputStram, the stream to use to write the given file.
+   */
+  private static void addFolderToZip(final String path, final String srcFolder, final ZipOutputStream zip) {
+    File folder = new File(srcFolder);
+    String[] fileListe = folder.list();
+    for (String string : fileListe) {
+      addToZip(path, srcFolder + "/" + string, zip);
+    }
+  }
+}

+ 96 - 0
src/main/java/de/mcs/utils/GetEnviroment.java

@@ -0,0 +1,96 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+
+/**
+ * Environment class simulates the System.getenv() method which is deprecated on
+ * java 1.4.2.
+ * 
+ * @author v-josp
+ */
+public final class GetEnviroment {
+    /** prevent instancing. */
+    private GetEnviroment() {
+    }
+
+    /** result of all enviornment variables. */
+    private static BufferedReader commandResult;
+
+    /** map with all commands. */
+    private static HashMap<String, String> commandList;
+    static {
+        String cmd = null;
+        String os = null;
+
+        // getting the OS name
+        os = System.getProperty("os.name").toLowerCase();
+
+        // according to OS set the command to execute
+        if (os.startsWith("windows")) {
+            cmd = "cmd /c SET";
+        } else {
+            cmd = "env";
+        }
+
+        try {
+            // execute the command and get the result in the form of InputStream
+            Process p = Runtime.getRuntime().exec(cmd);
+
+            // parse the InputStream data
+            InputStreamReader isr = new InputStreamReader(p.getInputStream());
+            commandResult = new BufferedReader(isr);
+        } catch (Exception e) {
+            System.out.println("OSEnvironment.class error: " + cmd + ":" + e);
+        }
+        commandList = new HashMap<String, String>();
+
+        String line = null;
+        try {
+            while ((line = commandResult.readLine()) != null) {
+                String key = line.substring(0, line.indexOf('='));
+                String value = line.substring(line.indexOf('=') + 1);
+                commandList.put(key, value);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * This method is used to get the path of the given enviornment variable.
+     * This method tries to simulates the System.getenv() which is deprecated on
+     * java 1.4.2
+     * 
+     * @param envName
+     *            name of the environment variable
+     * @param defaultValue
+     *            default value
+     * @return String
+     */
+    public static String getenv(final String envName, final String defaultValue) {
+        String value = defaultValue;
+        if (commandList.containsKey(envName)) {
+            value = (String) commandList.get(envName);
+        }
+        return value;
+    }
+}

+ 205 - 0
src/main/java/de/mcs/utils/HTMLEncoder.java

@@ -0,0 +1,205 @@
+/**
+ * 
+ */
+package de.mcs.utils;
+
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+
+/**
+ * @author w.klaas
+ * 
+ */
+public class HTMLEncoder {
+
+  private static final char c[] = { '<', '>', '&', '\"' };
+  private static final String expansion[] = { "&lt;", "&gt;", "&amp;", "&quot;" };
+
+  public static String HTMLEncode(String s) {
+    if (s == null) {
+      return null;
+    }
+    StringBuffer st = new StringBuffer();
+    for (int i = 0; i < s.length(); i++) {
+      boolean copy = true;
+      char ch = s.charAt(i);
+      for (int j = 0; j < c.length; j++) {
+        if (c[j] == ch) {
+          st.append(expansion[j]);
+          copy = false;
+          break;
+        }
+      }
+      if (copy)
+        st.append(ch);
+    }
+    return st.toString();
+  }
+
+  /**
+   * This method is overriden to map any character entities, such as &lt; to
+   * &amp;lt;. <code>super.output</code> will be invoked to write the content.
+   * 
+   * @param string
+   *          then string to encode
+   * @return the encoded string
+   * @since 1.3
+   */
+  public static String encode(String string) {
+    if (string == null) {
+      return null;
+    }
+    char[] chars = string.toCharArray();
+    StringWriter output = new StringWriter();
+    int last = 0;
+    int length = chars.length;
+    for (int counter = 0; counter < length; counter++) {
+      // This will change, we need better support character level
+      // entities.
+      switch (chars[counter]) {
+      // Character level entities.
+      case '<':
+        if (counter > last) {
+          output.write(chars, last, counter - last);
+        }
+        last = counter + 1;
+        output.write("&lt;");
+        break;
+      case '>':
+        if (counter > last) {
+          output.write(chars, last, counter - last);
+        }
+        last = counter + 1;
+        output.write("&gt;");
+        break;
+      case '&':
+        if (counter > last) {
+          output.write(chars, last, counter - last);
+        }
+        last = counter + 1;
+        output.write("&amp;");
+        break;
+      case '"':
+        if (counter > last) {
+          output.write(chars, last, counter - last);
+        }
+        last = counter + 1;
+        output.write("&quot;");
+        break;
+      // Special characters
+      case '\n':
+        if (counter > last) {
+          output.write(chars, last, counter - last);
+        }
+        last = counter + 1;
+        output.write("<br/>");
+        break;
+      case '\t':
+      case '\r':
+        break;
+      default:
+        if (chars[counter] < ' ' || chars[counter] > 127) {
+          if (counter > last) {
+            output.write(chars, last, counter - last);
+          }
+          last = counter + 1;
+          // If the character is outside of ascii, write the
+          // numeric value.
+          output.write("&#");
+          output.write(String.valueOf((int) chars[counter]));
+          output.write(";");
+        }
+        break;
+      }
+    }
+    if (last < length) {
+      output.write(chars, last, length - last);
+    }
+    return output.toString();
+  }
+
+  public static String URLEncode(String string) {
+    if (string == null) {
+      return null;
+    }
+    try {
+      return URLEncoder.encode(string, "UTF-8");
+    } catch (UnsupportedEncodingException e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  public static String URLDencode(String string) {
+    if (string == null) {
+      return null;
+    }
+    try {
+      return URLDecoder.decode(string, "UTF-8");
+    } catch (UnsupportedEncodingException e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  public static String stringToHTMLString(String string) {
+    StringBuffer sb = new StringBuffer(string.length());
+    // true if last char was blank
+    boolean lastWasBlankChar = false;
+    int len = string.length();
+    char c;
+
+    for (int i = 0; i < len; i++) {
+      c = string.charAt(i);
+      if (c == ' ') {
+        // blank gets extra work,
+        // this solves the problem you get if you replace all
+        // blanks with &nbsp;, if you do that you loss
+        // word breaking
+        if (lastWasBlankChar) {
+          lastWasBlankChar = false;
+          sb.append("&nbsp;");
+        } else {
+          lastWasBlankChar = true;
+          sb.append(' ');
+        }
+      } else {
+        lastWasBlankChar = false;
+        //
+        // HTML Special Chars
+        if (c == '"')
+          sb.append("&quot;");
+        else if (c == '&')
+          sb.append("&amp;");
+        else if (c == '<')
+          sb.append("&lt;");
+        else if (c == '>')
+          sb.append("&gt;");
+        else if (c == '\r')
+          ;
+        else if (c == '\n')
+          // Handle Newline
+          sb.append("<br/>");
+        else {
+          int ci = 0xffff & c;
+          if (ci < 160)
+            // nothing special only 7 Bit
+            sb.append(c);
+          else {
+            // Not 7 Bit use the unicode system
+            sb.append("&#");
+            sb.append(new Integer(ci).toString());
+            sb.append(';');
+          }
+        }
+      }
+    }
+    return sb.toString();
+  }
+
+  public static void main(String[] args) {
+    System.out.println(stringToHTMLString(" abcdefghijklmnopqrstuvwxyzüäöÜÄÖß<>;&%$§\r\n \n"));
+  }
+}

+ 105 - 0
src/main/java/de/mcs/utils/MD5Utils.java

@@ -0,0 +1,105 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2006 by MCS
+ * -------------------------------------- Created on 10.01.2006 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Some helper methode to build and convert a MD5 hash. For building MD5 from
+ * files see de.mcs.utils.Files#computeMD5FromFile(String)
+ * 
+ * @author w.klaas
+ * 
+ */
+public final class MD5Utils {
+
+    /**
+     * to prevent instancing.
+     */
+    private MD5Utils() {
+    }
+
+    /**
+     * Building the md5 bytes from byte array.
+     * 
+     * @param s
+     *            data byte array
+     * @return byte[] byte array with md5
+     */
+    public static byte[] md5Bytes(final byte[] s) {
+	// Build MD5-hashcode
+	MessageDigest md;
+	try {
+	    md = MessageDigest.getInstance("MD5");
+	    md.update(s);
+	    byte[] digest = md.digest();
+
+	    return digest;
+	} catch (NoSuchAlgorithmException e) {
+	    e.printStackTrace();
+	}
+	return null;
+    }
+
+    /**
+     * Building the md5 bytes from byte array.
+     * 
+     * @param s
+     *            data byte array
+     * @return byte[] byte array with md5
+     */
+    public static byte[] md5Bytes(final byte[][] s) {
+	// Build MD5-hashcode
+	MessageDigest md;
+	try {
+	    md = MessageDigest.getInstance("MD5");
+	    for (byte[] bs : s) {
+		md.update(bs);
+	    }
+
+	    byte[] digest = md.digest();
+
+	    return digest;
+	} catch (NoSuchAlgorithmException e) {
+	    e.printStackTrace();
+	}
+	return null;
+    }
+
+    /**
+     * convinient functions to convert a MD5 stirng to the byte array.
+     * 
+     * @param md5String
+     *            the string to convert.
+     * @return byte[]
+     */
+    public static byte[] md5StringTobytes(final String md5String) {
+	return StringFormat.fromHexString(md5String);
+    }
+
+    /**
+     * convinient functions to convert a MD5 digest (as byte[]) to a string.
+     * 
+     * @param digest
+     *            the byte[] to convert.
+     * @return String
+     */
+    public static String md5BytesToString(final byte[] digest) {
+	return StringFormat.toHexString(digest);
+    }
+}

+ 65 - 0
src/main/java/de/mcs/utils/Macro.java

@@ -0,0 +1,65 @@
+/**
+ * 
+ */
+package de.mcs.utils;
+
+/**
+ * @author w.klaas
+ */
+public class Macro {
+
+  private String value;
+  private String name;
+
+  /**
+   * Constructs a new macro
+   * 
+   * @param macroName
+   *          name of the macro
+   * @param value
+   *          value for substitution
+   */
+  public Macro(String macroName, String value) {
+    this.name = macroName;
+    this.value = value;
+  }
+
+  /**
+   * @return the value for substitution
+   */
+  public String getValue() {
+    return value;
+  }
+
+  /**
+   * @param value
+   *          setting a new value for this macro
+   */
+  public void setValue(String value) {
+    this.value = value;
+  }
+
+  /**
+   * @return name of this macro.
+   */
+  public String getMacroName() {
+    return name;
+  }
+
+  /**
+   * @return same as getMacroName(), but with the {} delimiter.
+   */
+  public String getMacroDefinition() {
+    StringBuilder b = new StringBuilder();
+    b.append('{');
+    b.append(getMacroName());
+    b.append('}');
+    return b.toString();
+  }
+
+  public String toString() {
+    StringBuilder b = new StringBuilder();
+    b.append(String.format("macro: {name: %s; value: %s}", name, value));
+    return b.toString();
+  }
+}

+ 51 - 0
src/main/java/de/mcs/utils/MacroReplacer.java

@@ -0,0 +1,51 @@
+package de.mcs.utils;
+
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * This class is a helper class to replace macros marked with ${xxxx} with
+ * values from a a key value map. You can use internationalisation on the
+ * values. like: "default:normale","en:normale","de:normal"
+ * 
+ * @author w.klaas
+ *
+ */
+public class MacroReplacer {
+
+  private Locale locale;
+
+  public MacroReplacer(Locale locale) {
+    this.locale = locale;
+  }
+
+  public String replace(final String source, final Map<String, String> replaceMap) {
+    String sourceStr = source;
+    int pos = sourceStr.indexOf("${");
+    while (pos >= 0) {
+      String left = sourceStr.substring(0, pos);
+      int endpos = sourceStr.indexOf("}", pos);
+      String macro = sourceStr.substring(pos + 2, endpos);
+
+      if (replaceMap.containsKey(macro)) {
+        macro = replaceMap.get(macro);
+        if (macro.contains(",")) {
+          Map<String, String> localeStrings = StringUtils.csvStringToPropMap(macro, ',', '\"', ':');
+          if (localeStrings.containsKey("default")) {
+            macro = localeStrings.get("default");
+          }
+          if (localeStrings.containsKey(locale.getLanguage())) {
+            macro = localeStrings.get(locale.getLanguage());
+          }
+        }
+      }
+
+      String right = sourceStr.substring(endpos + 1);
+      sourceStr = left + macro + right;
+
+      pos = sourceStr.indexOf("${");
+    }
+
+    return sourceStr;
+  }
+}

+ 120 - 0
src/main/java/de/mcs/utils/Macros.java

@@ -0,0 +1,120 @@
+package de.mcs.utils;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This implements a iterable list of unique {@link Macro}.
+ * 
+ * @author w.klaas
+ */
+public class Macros implements Iterable<Macro> {
+
+  private List<Macro> macroTable;
+
+  /**
+   * Default constructor for the macro list
+   */
+  public Macros() {
+    this.macroTable = new ArrayList<>();
+  }
+
+  /**
+   * adding a new macro definition to this list. If the name is already present,
+   * only the value will be changed.
+   * 
+   * @param name
+   *          name of the new macro
+   * @param value
+   *          substitution value of the macro
+   */
+  public void addMacro(String name, String value) {
+    for (Iterator<Macro> iterator = macroTable.iterator(); iterator.hasNext();) {
+      Macro macro = (Macro) iterator.next();
+      if (macro.getMacroName().equals(name)) {
+        macro.setValue(value);
+        return;
+      }
+    }
+    macroTable.add(new Macro(name, value));
+  }
+
+  /**
+   * adding a new macro definition to this list. If the name is already present,
+   * only the value will be changed.
+   * 
+   * @param macro
+   *          the macro to be added
+   */
+  public void addMacro(Macro macro) {
+    for (Iterator<Macro> iterator = macroTable.iterator(); iterator.hasNext();) {
+      Macro myMacro = (Macro) iterator.next();
+      if (myMacro.getMacroName().equals(macro.getMacroName())) {
+        myMacro.setValue(macro.getValue());
+        return;
+      }
+    }
+    macroTable.add(macro);
+  }
+
+  /**
+   * @return iterator over this list of macros
+   */
+  public Iterator<Macro> iterator() {
+    return macroTable.iterator();
+  }
+
+  /**
+   * substitue a string with macros. All Macros should be name in the string as
+   * {macroname}.
+   * 
+   * @param text
+   *          the original string with the macro definitions in
+   * @return the substituted string
+   */
+  public String substitute(final String text) {
+    StringBuilder stringBuilder = new StringBuilder(text);
+    for (Macro macro : this) {
+      int index = stringBuilder.indexOf(macro.getMacroDefinition());
+      if (index != -1) {
+        stringBuilder.replace(index, index + macro.getMacroDefinition().length(), macro.getValue());
+      }
+    }
+    return stringBuilder.toString();
+  }
+
+  /**
+   * getting a single macro from the list of macros
+   * 
+   * @param name
+   *          the name of the macros to get
+   * @return the desired macro or <code>null</code> if the macro is not present.
+   */
+  public Macro getMacro(String name) {
+    for (Iterator<Macro> iterator = macroTable.iterator(); iterator.hasNext();) {
+      Macro macro = (Macro) iterator.next();
+      if (macro.getMacroName().equals(name)) {
+        return macro;
+      }
+    }
+    return null;
+  }
+
+  public String toString() {
+    StringBuilder b = new StringBuilder();
+    b.append("macros: [");
+    boolean first = true;
+    for (Macro macro : macroTable) {
+      if (!first) {
+        b.append(';');
+      } else {
+        first = !first;
+      }
+      b.append(macro.toString());
+    }
+    b.append(']');
+    return b.toString();
+  }
+
+}

+ 22 - 0
src/main/java/de/mcs/utils/Maputils.java

@@ -0,0 +1,22 @@
+/**
+ * 
+ */
+package de.mcs.utils;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * @author w.klaas
+ * 
+ */
+public class Maputils {
+
+  public static String findEntry(Map<String, String> form, String regexString) {
+    for (Entry<String, String> entry : form.entrySet()) {
+      if (entry.getKey().matches(regexString))
+        return entry.getValue();
+    }
+    return null;
+  }
+}

+ 31 - 0
src/main/java/de/mcs/utils/MimetypeFactory.java

@@ -0,0 +1,31 @@
+/**
+ * 
+ */
+package de.mcs.utils;
+
+import java.io.File;
+
+import javax.activation.MimetypesFileTypeMap;
+
+/**
+ * @author w.klaas
+ * 
+ */
+public class MimetypeFactory {
+
+  public static final String DEFAULT_MIMETYPE = "application/octet-stream";
+
+  private static MimetypesFileTypeMap MimetypeMap = new MimetypesFileTypeMap();
+
+  public static String getFileMimtype(final File file) {
+    String mimetype = MimetypeMap.getContentType(file);
+    if (mimetype.equals(DEFAULT_MIMETYPE)) {
+      return getFileMimtype(file.getAbsolutePath().toLowerCase());
+    }
+    return mimetype;
+  }
+
+  public static String getFileMimtype(final String filename) {
+    return MimetypeMap.getContentType(filename.toLowerCase());
+  }
+}

+ 83 - 0
src/main/java/de/mcs/utils/NamedList.java

@@ -0,0 +1,83 @@
+/**
+ * 
+ */
+package de.mcs.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * @author w.klaas
+ * 
+ */
+public class NamedList {
+
+  private Map<String, String> list;
+
+  public NamedList() {
+    list = new HashMap<String, String>();
+  }
+
+  public void add(final String key, final String value) {
+    list.put(key, value);
+  }
+
+  public String get(final String key) {
+    return list.get(key);
+  }
+
+  public String remove(final String key) {
+    return list.remove(key);
+  }
+
+  public String toString() {
+    StringBuilder b = new StringBuilder();
+    for (Entry<String, String> entry : list.entrySet()) {
+      b.append(entry.getKey());
+      b.append("=");
+      try {
+        if (entry.getValue() != null) {
+          b.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
+        }
+      } catch (UnsupportedEncodingException e) {
+      }
+      b.append(";");
+    }
+    return b.substring(0, b.length() - 1).toString();
+  }
+
+  public void parseString(final String line) {
+    String[] keyValues = line.split(";");
+    for (String entry : keyValues) {
+      if ((entry != null) && !entry.equals("")) {
+        String[] entryValues = entry.split("=");
+        if (entryValues.length > 0) {
+          String key = entryValues[0];
+          String value = null;
+          if (entryValues.length > 1) {
+            try {
+              value = URLDecoder.decode(entryValues[1], "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+            }
+          }
+          add(key, value);
+        }
+      }
+    }
+  }
+
+  public static void main(String[] args) {
+    NamedList list = new NamedList();
+    list.add("test1", "test1");
+    list.add("test2", "test;:#'+*!\"§$%&/()=");
+    System.out.println(list.toString());
+
+    NamedList list2 = new NamedList();
+    list2.parseString(list.toString());
+    System.out.println(list2.toString());
+  }
+}

+ 87 - 0
src/main/java/de/mcs/utils/NumberHelper.java

@@ -0,0 +1,87 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2007 by MCS
+ * -------------------------------------- Created on 23.01.2007 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/**
+ * 
+ */
+package de.mcs.utils;
+
+/**
+ * This class helps to convert numbers.
+ * 
+ * @author w.klaas
+ * 
+ */
+public class NumberHelper {
+
+    private static final String CONVERT_UNITS = "KMGkmg";
+
+    private static final String CONVERT_TIMES = "YMDhms";
+
+    public static int parseInt(final String valueStr) {
+        String newValue = valueStr;
+        int unit = 1;
+        if (CONVERT_UNITS.indexOf(valueStr.charAt(valueStr.length() - 1)) >= 0) {
+            switch (valueStr.charAt(valueStr.length() - 1)) {
+            case 'K':
+                unit = 1024;
+                break;
+            case 'M':
+                unit = 1024 * 1024;
+                break;
+            case 'G':
+                unit = 1024 * 1024 * 1024;
+                break;
+            case 'k':
+                unit = 1000;
+                break;
+            case 'm':
+                unit = 1000 * 1000;
+                break;
+            case 'g':
+                unit = 1000 * 1000 * 1000;
+                break;
+            default:
+                break;
+            }
+            newValue = newValue.substring(0, newValue.length() - 1);
+        }
+        return Integer.parseInt(newValue) * unit;
+    }
+
+    public static long parseTime(final String valueStr) {
+        String newValue = valueStr;
+        int unit = 1;
+        if (CONVERT_TIMES.indexOf(valueStr.charAt(valueStr.length() - 1)) >= 0) {
+            switch (valueStr.charAt(valueStr.length() - 1)) {
+            case 'D':
+                unit = unit * 24;
+            case 'h':
+                unit = unit * 60;
+            case 'm':
+                unit = unit * 60;
+            case 's':
+                unit = unit * 1000;
+                break;
+            default:
+                break;
+            }
+            newValue = newValue.substring(0, newValue.length() - 1);
+        }
+        return Long.parseLong(newValue) * unit;
+    }
+
+}

+ 82 - 0
src/main/java/de/mcs/utils/OSInformations.java

@@ -0,0 +1,82 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+/**
+ * Hetting informations about the underrlying OS.
+ * 
+ * @author w.klaas
+ * 
+ */
+public final class OSInformations {
+
+    /** prevent instancing. */
+    private OSInformations() {
+
+    }
+
+    /** the OS version. */
+    private static String myOSVersion = System.getProperty("os.name");
+
+    /** the framework runs on windows. */
+    private static boolean bWin32 = false;
+    static {
+        bWin32 = (System.getProperty("os.name").toUpperCase()
+                .startsWith("WINDOWS"));
+    }
+
+    /** the framework runs on MAC OS X. */
+    private static boolean bMACOSX = false;
+    static {
+        bMACOSX = (System.getProperty("os.name").toUpperCase().substring(0, 3)
+                .equals("MAC"));
+    }
+
+    /**
+     * checking the underlying OS.
+     * 
+     * @param args
+     *            not used.
+     */
+    public static void main(final String[] args) {
+        System.out.print("Win32:");
+        System.out.println(bWin32);
+        System.out.print("MacOSX:");
+        System.out.println(bMACOSX);
+    }
+
+    /**
+     * @return is this vm running on MACOSX
+     */
+    public static boolean isMACOSX() {
+        return bMACOSX;
+    }
+
+    /**
+     * @return is this vm running on MACOSX
+     */
+    public static boolean isWin32() {
+        return bWin32;
+    }
+
+    /**
+     * @return getting the OS version string.
+     */
+    public static String getOSVersion() {
+        return myOSVersion;
+    }
+}

+ 201 - 0
src/main/java/de/mcs/utils/PortTest.java

@@ -0,0 +1,201 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2007 by MCS
+ * -------------------------------------- Created on 21.11.2007 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/**
+ * 
+ */
+package de.mcs.utils;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.ArrayList;
+
+/**
+ * @author w.klaas
+ * 
+ */
+public class PortTest extends Thread {
+
+  private String server;
+
+  private int port;
+
+  private boolean result;
+
+  public PortTest(String server, int port) {
+    this.server = server;
+    this.port = port;
+  }
+
+  public void setServer(String param) {
+    this.server = param;
+  }
+
+  public String getServer() {
+    return server;
+  }
+
+  public void setPort(int param) {
+    this.port = param;
+  }
+
+  public int getPort() {
+    return port;
+  }
+
+  public boolean checkPortWithoutPing() {
+    // long timeStart = System.currentTimeMillis();
+    boolean success = false;
+    try {
+      Socket s = new Socket(server, port);
+      s.close();
+      success = true;
+    } catch (IOException e) {
+      success = false;
+    }
+    // long timeEnd = System.currentTimeMillis();
+    // System.out.println("Portcheck w/o ping: " + (timeEnd - timeStart));
+    return success;
+  }
+
+  public boolean checkPortWithPing() {
+    long timeStart = System.currentTimeMillis();
+    boolean success = false;
+    if (pingServer()) {
+      success = checkPortWithoutPing();
+    } else
+      success = false;
+    long timeEnd = System.currentTimeMillis();
+    System.out.println("Portcheck w ping: " + (timeEnd - timeStart));
+    return success;
+  }
+
+  private boolean isLinux() {
+    return !System.getProperty("os.name").toLowerCase().contains("windows");
+  }
+
+  public boolean pingServer() {
+    long timeStart = System.currentTimeMillis();
+    boolean success = false;
+
+    ProcessBuilder pb = null;
+    pb = new ProcessBuilder();
+    String[] cmd = null;
+
+    if (isLinux())
+      cmd = new String[] { "ping", "-c", "1", server };
+    else
+      // windows
+      cmd = new String[] { "ping", "-w", "250", "-n", "1", server };
+
+    pb.command(cmd);
+    Process p;
+
+    try {
+      p = pb.start();
+    } catch (IOException e) {
+      return success;
+    }
+    if (p != null) {
+      try {
+        p.waitFor();
+        success = p.exitValue() == 0;
+      } catch (InterruptedException e) {
+        success = false;
+      }
+    }
+    long timeEnd = System.currentTimeMillis();
+    System.out.println("Ping: " + (timeEnd - timeStart));
+    return success;
+  }
+
+  public static void main(String[] args) {
+    ArrayList<String> results = new ArrayList<String>();
+
+    for (int x = 0; x < 65000; x++) {
+      ArrayList<PortTest> list = new ArrayList<PortTest>();
+      for (int i = 0; i < 100; i++) {
+        PortTest porttest = new PortTest("127.0.0.1", i + (x * 100));
+        porttest.start();
+        list.add(porttest);
+      }
+      for (PortTest test : list) {
+        while (test.isAlive()) {
+        }
+        if (test.getResult()) {
+          results.add("Port " + test.getPort() + " is open.");
+        } else {
+          // results.add("Port " + test.getPort() + " is closed.");
+        }
+      }
+      for (String string : results) {
+        System.out.println(string);
+      }
+      results.clear();
+    }
+
+    // portcheck = new PortTest("192.168.0.112", 445);
+    // // server does not exist
+    // System.out.println("SERVER DOES NOT EXIST");
+    // System.out.println(">>>with Ping: " + portcheck.checkPortWithPing());
+    // System.out.println(">>>w/o Ping: " +
+    // portcheck.checkPortWithoutPing());
+    //
+    // portcheck.setServer("localhost");
+    // // server does exist, port does exist
+    // System.out.println("SERVER EXISTS, PORT OPEN");
+    // System.out.println(">>>with Ping: " + portcheck.checkPortWithPing());
+    // System.out.println(">>>w/o Ping: " +
+    // portcheck.checkPortWithoutPing());
+    //
+    // portcheck.setPort(4711);
+    // // server does exist, port does not
+    // System.out.println("SERVER EXISTS, PORT CLOSED");
+    // System.out.println(">>>with Ping: " + portcheck.checkPortWithPing());
+    // System.out.println(">>>w/o Ping: " + portcheck.checkPortWithoutPing());
+  }
+
+  public void run() {
+    result = checkPortWithoutPing();
+  }
+
+  /**
+   * @return the result
+   */
+  public boolean getResult() {
+    return result;
+  }
+
+  public static int nextFreePort(int min, int max) {
+    for (int x = min; x <= max; x++) {
+      PortTest porttest = new PortTest("127.0.0.1", x);
+      porttest.start();
+      while (porttest.isAlive()) {
+        try {
+          sleep(500);
+        } catch (InterruptedException e) {
+          e.printStackTrace();
+          return -1;
+        }
+      }
+      if (!porttest.getResult()) {
+        return x;
+      }
+    }
+
+    return -1;
+  }
+}

+ 237 - 0
src/main/java/de/mcs/utils/PropertiesHelper.java

@@ -0,0 +1,237 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Some helper functions for properties like getting all keys with a regex.
+ * 
+ * @author w.klaas
+ * 
+ */
+public class PropertiesHelper extends Properties {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3239128704806211452L;
+
+  /**
+   * Searching for all Keys with the regex string.
+   * 
+   * @param regex
+   *            regular expression for the keys to find
+   * @return String[] array with all found keys
+   */
+  @SuppressWarnings("rawtypes")
+  public final String[] getKeys(final String regex) {
+    ArrayList<String> list = new ArrayList<String>();
+    for (Iterator iter = keySet().iterator(); iter.hasNext();) {
+      String key = (String) iter.next();
+      if (key.matches(regex)) {
+        list.add(key);
+      }
+    }
+    return (String[]) list.toArray(new String[0]);
+  }
+
+  /**
+   * Extracting all properties into a new Propertie class with begin with the
+   * prefix keyPrefix.
+   * 
+   * @param keyPrefix
+   *            the prefix to use.
+   * @return Properties
+   */
+  public final Properties extractProperties(final String keyPrefix) {
+    Properties mediaProps = new Properties();
+    String[] mediaKeyNames = getKeys(keyPrefix + ".*");
+    for (int j = 0; j < mediaKeyNames.length; j++) {
+      String key = mediaKeyNames[j];
+      String value = getProperty(key);
+      key = key.substring(key.indexOf(keyPrefix + ".") + (keyPrefix + ".").length());
+      mediaProps.setProperty(key, value);
+    }
+    return mediaProps;
+  }
+
+  /**
+   * Storing a properties class in a Stream in sorted manner.
+   * 
+   * @param out
+   *            the output stream
+   * @param comments
+   *            the comments header.
+   * @throws IOException
+   *             if something goes wrong.
+   */
+  @SuppressWarnings("rawtypes")
+  public final void storeSorted(final OutputStream out, final String comments) throws IOException {
+
+    BufferedWriter awriter;
+    awriter = new BufferedWriter(new OutputStreamWriter(out, "8859_1"));
+    if (comments != null) {
+      writeln(awriter, "#" + comments);
+    }
+    writeln(awriter, "#" + new Date().toString());
+    SortedSet<Object> mySet = new TreeSet<Object>(new Comparator<Object>() {
+
+      public int compare(final Object o1, final Object o2) {
+        if ((o1 instanceof String) && (o2 instanceof String)) {
+          String s1 = (String) o1;
+          String s2 = (String) o2;
+          return s1.compareTo(s2);
+        }
+        return 0;
+      }
+
+    });
+    mySet.addAll(keySet());
+    for (Iterator iter = mySet.iterator(); iter.hasNext();) {
+      String key = (String) iter.next();
+      String val = (String) get(key);
+      key = saveConvert(key, true);
+
+      /*
+       * No need to escape embedded and trailing spaces for value, hence
+       * pass false to flag.
+       */
+      val = saveConvert(val, false);
+      writeln(awriter, key + "=" + val);
+    }
+    awriter.flush();
+  }
+
+  /**
+   * write aa string and a newLine after.
+   * 
+   * @param bw
+   *            where to write.
+   * @param s
+   *            what to write.
+   * @throws IOException
+   *             if something goes wrong.
+   */
+  private static void writeln(final BufferedWriter bw, final String s) throws IOException {
+    bw.write(s);
+    bw.newLine();
+  }
+
+  /**
+   * Converts unicodes to encoded &#92;uxxxx and escapes special characters
+   * with a preceding slash.
+   * 
+   * @param theString
+   *            the string to convert.
+   * @param escapeSpace
+   *            escape the spaces too.
+   * @return the converted String.
+   */
+  private String saveConvert(final String theString, final boolean escapeSpace) {
+    int len = theString.length();
+    int bufLen = len * 2;
+    if (bufLen < 0) {
+      bufLen = Integer.MAX_VALUE;
+    }
+    StringBuffer outBuffer = new StringBuffer(bufLen);
+
+    for (int x = 0; x < len; x++) {
+      char aChar = theString.charAt(x);
+      // Handle common case first, selecting largest block that
+      // avoids the specials below
+      if ((aChar > 61) && (aChar < 127)) {
+        if (aChar == '\\') {
+          outBuffer.append('\\');
+          outBuffer.append('\\');
+          continue;
+        }
+        outBuffer.append(aChar);
+        continue;
+      }
+      switch (aChar) {
+      case ' ':
+        if (x == 0 || escapeSpace) {
+          outBuffer.append('\\');
+        }
+        outBuffer.append(' ');
+        break;
+      case '\t':
+        outBuffer.append('\\');
+        outBuffer.append('t');
+        break;
+      case '\n':
+        outBuffer.append('\\');
+        outBuffer.append('n');
+        break;
+      case '\r':
+        outBuffer.append('\\');
+        outBuffer.append('r');
+        break;
+      case '\f':
+        outBuffer.append('\\');
+        outBuffer.append('f');
+        break;
+      case '=': // Fall through
+      case ':': // Fall through
+      case '#': // Fall through
+      case '!':
+        outBuffer.append('\\');
+        outBuffer.append(aChar);
+        break;
+      default:
+        if ((aChar < 0x0020) || (aChar > 0x007e)) {
+          outBuffer.append('\\');
+          outBuffer.append('u');
+          outBuffer.append(toHex((aChar >> 12) & 0xF));
+          outBuffer.append(toHex((aChar >> 8) & 0xF));
+          outBuffer.append(toHex((aChar >> 4) & 0xF));
+          outBuffer.append(toHex(aChar & 0xF));
+        } else {
+          outBuffer.append(aChar);
+        }
+      }
+    }
+    return outBuffer.toString();
+  }
+
+  /**
+   * Convert a nibble to a hex character.
+   * 
+   * @param nibble
+   *            the nibble to convert.
+   * @return char
+   */
+  private static char toHex(final int nibble) {
+    return HEXDIGIT[(nibble & 0xF)];
+  }
+
+  /** A table of hex digits. */
+  private static final char[] HEXDIGIT = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
+      'F' };
+
+}

+ 107 - 0
src/main/java/de/mcs/utils/SHA256Utils.java

@@ -0,0 +1,107 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2006 by MCS
+ * -------------------------------------- Created on 10.01.2006 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Some helper methode to build and convert a MD5 hash. For building MD5 from
+ * files see de.mcs.utils.Files#computeMD5FromFile(String)
+ * 
+ * @author w.klaas
+ * 
+ */
+public final class SHA256Utils {
+
+  private static final String ALGORITHM = "SHA-256";
+
+  /**
+   * to prevent instancing.
+   */
+  private SHA256Utils() {
+  }
+
+  /**
+   * Building the md5 bytes from byte array.
+   * 
+   * @param s
+   *          data byte array
+   * @return byte[] byte array with md5
+   */
+  public static byte[] shaBytes(final byte[] s) {
+    // Build sha-hashcode
+    MessageDigest md;
+    try {
+      md = MessageDigest.getInstance(ALGORITHM);
+      md.update(s);
+      byte[] digest = md.digest();
+
+      return digest;
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /**
+   * Building the md5 bytes from byte array.
+   * 
+   * @param s
+   *          data byte array
+   * @return byte[] byte array with md5
+   */
+  public static byte[] shaBytes(final byte[][] s) {
+    // Build MD5-hashcode
+    MessageDigest md;
+    try {
+      md = MessageDigest.getInstance(ALGORITHM);
+      for (byte[] bs : s) {
+        md.update(bs);
+      }
+
+      byte[] digest = md.digest();
+
+      return digest;
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /**
+   * convinient functions to convert a MD5 stirng to the byte array.
+   * 
+   * @param md5String
+   *          the string to convert.
+   * @return byte[]
+   */
+  public static byte[] shaStringTobytes(final String md5String) {
+    return StringFormat.fromHexString(md5String);
+  }
+
+  /**
+   * convinient functions to convert a MD5 digest (as byte[]) to a string.
+   * 
+   * @param digest
+   *          the byte[] to convert.
+   * @return String
+   */
+  public static String shaBytesToString(final byte[] digest) {
+    return StringFormat.toHexString(digest);
+  }
+}

+ 303 - 0
src/main/java/de/mcs/utils/SoftHashMap.java

@@ -0,0 +1,303 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 23.04.2005 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * Implements a Map with only soft references backuped by a hashmap.
+ * 
+ * @author w.klaas
+ */
+
+public class SoftHashMap<K, V> extends AbstractMap<K, V> {
+  /** the map to store. */
+  private Map<K, Reference<V>> m = null;
+
+  /** the reference queue. */
+  private ReferenceQueue q = new ReferenceQueue();
+
+  /**
+   * Constructs an empty <tt>HashMap</tt> with the default initial capacity (16)
+   * and the default load factor (0.75).
+   */
+  public SoftHashMap() {
+    m = new HashMap<K, Reference<V>>();
+  }
+
+  /**
+   * Constructs an empty <tt>HashMap</tt> with the specified initial capacity
+   * and the default load factor (0.75).
+   * 
+   * @param initialCapacity
+   *          the initial capacity.
+   */
+  public SoftHashMap(final int initialCapacity) {
+    m = new HashMap<K, Reference<V>>(initialCapacity);
+  }
+
+  /**
+   * Constructs an empty <tt>HashMap</tt> with the specified initial capacity
+   * and load factor.
+   * 
+   * @param initialCapacity
+   *          The initial capacity.
+   * @param loadFactor
+   *          The load factor.
+   */
+  public SoftHashMap(final int initialCapacity, final float loadFactor) {
+    m = new HashMap<K, Reference<V>>(initialCapacity, loadFactor);
+  }
+
+  /**
+   * Returns the value to which this map maps the specified key. Returns
+   * <tt>null</tt> if the map contains no mapping for this key. A return value
+   * of <tt>null</tt> does not <i>necessarily</i> indicate that the map contains
+   * no mapping for the key; it's also possible that the map explicitly maps the
+   * key to <tt>null</tt>. The <tt>containsKey</tt> operation may be used to
+   * distinguish these two cases.
+   * 
+   * <p>
+   * More formally, if this map contains a mapping from a key <tt>k</tt> to a
+   * value <tt>v</tt> such that <tt>(key==null ? k==null :
+   * key.equals(k))</tt>, then this method returns <tt>v</tt>; otherwise it
+   * returns <tt>null</tt>. (There can be at most one such mapping.)
+   * 
+   * @param key
+   *          key whose associated value is to be returned.
+   * @return the value to which this map maps the specified key, or
+   *         <tt>null</tt> if the map contains no mapping for this key.
+   * 
+   * @see #containsKey(Object)
+   */
+  public final V get(final Object key) {
+    Reference<V> res = m.get(key);
+    return (res == null ? null : res.get());
+  }
+
+  /*
+   * @see Map#put(K, V)
+   */
+  public final V put(final K key, final V value) {
+    processQueue();
+    Reference<V> ref = new SoftEntry(key, value, q);
+    Reference<V> res = m.put(key, ref);
+    return res == null ? null : res.get();
+  }
+
+  /** the entry set. */
+  private Set entrySet = null;
+
+  /**
+   * getting the entry set.
+   * 
+   * @return Set
+   */
+  public final Set entrySet() {
+    if (entrySet == null) {
+      entrySet = new EntrySet();
+    }
+    return entrySet;
+  }
+
+  /**
+   * processing the softentry queue.
+   * 
+   */
+  private void processQueue() {
+    Reference r;
+    while ((r = q.poll()) != null) {
+      SoftEntry e = (SoftEntry) r;
+      m.remove(e.key);
+    }
+  }
+
+  /**
+   * @see java.util.Set#size()
+   */
+  public final int size() {
+    return entrySet().size();
+  }
+
+  /**
+   * @see java.util.Set#remove(java.lang.Object)
+   */
+  public final V remove(final Object key) {
+    processQueue();
+    Reference<V> res = m.remove(key);
+    return res == null ? null : res.get();
+  }
+
+  /**
+   * @see java.util.Map#clear()
+   */
+  public final void clear() {
+    processQueue();
+    m.clear();
+  }
+
+  /**
+   * this is the soft reference entry in the map.
+   * 
+   * @author w.klaas
+   * 
+   */
+  private static final class SoftEntry<K, T> extends SoftReference<T> {
+    /** neccessary so that freed objects can be removed. */
+    private K key;
+
+    /**
+     * private contructor to this entry.
+     * 
+     * @param aKey
+     *          the key for this entry
+     * @param value
+     *          the value
+     * @param q
+     *          the reference queue
+     */
+    private SoftEntry(final K aKey, final T value, final ReferenceQueue q) {
+      super(value, q);
+      this.key = aKey;
+    }
+  }
+
+  /**
+   * My set of entries.
+   * 
+   * @author w.klaas
+   * 
+   */
+  private class EntrySet<E> extends AbstractSet<E> {
+    /** the entry set to use. */
+    private Set entrySet = m.entrySet();
+
+    /**
+     * @see java.util.Set#size()
+     */
+    public int size() {
+      int s = 0;
+      for (Iterator<E> i = iterator(); i.hasNext(); i.next()) {
+        s++;
+      }
+      return s;
+    }
+
+    /**
+     * @see java.util.Set#isEmpty()
+     */
+    public boolean isEmpty() {
+      return !(iterator().hasNext());
+    }
+
+    /**
+     * @see java.util.Set#remove(java.lang.Object)
+     */
+    public boolean remove(final Object o) {
+      processQueue();
+      return super.remove(o);
+    }
+
+    /**
+     * @see java.util.Set#iterator()
+     */
+    public Iterator<E> iterator() {
+
+      return new Iterator() {
+        private Iterator it = entrySet.iterator();
+
+        private Entry next = null;
+
+        private Object value = null;
+
+        /*
+         * Strong reference to key, so that the GC will leave it alone as long
+         * as this Entry exists
+         */
+
+        public boolean hasNext() {
+          while (it.hasNext()) {
+            final Entry e = (Entry) it.next();
+            SoftEntry se = (SoftEntry) e.getValue();
+            value = null;
+            if ((se != null) && ((value = se.get()) == null)) {
+              /* Weak key has been cleared by GC */
+              continue;
+            }
+            next = new Map.Entry() {
+              public Object getKey() {
+                return e.getKey();
+              }
+
+              public Object getValue() {
+                return value;
+              }
+
+              public Object setValue(final Object v) {
+                Object res = value;
+                value = v;
+                e.setValue(new SoftEntry(e.getKey(), value, q));
+                return res;
+              }
+
+              public boolean equals(final Object x) {
+                if (!(x instanceof Map.Entry)) {
+                  return false;
+                }
+                Map.Entry e = (Map.Entry) x;
+                Object key = getKey();
+                return key == null ? e.getKey() == null
+                    : key.equals(e.getKey()) && value == null ? e.getValue() == null : value.equals(e.getValue());
+              }
+
+              public int hashCode() {
+                Object key = getKey();
+                return (((key == null) ? 0 : key.hashCode()) ^ ((value == null) ? 0 : value.hashCode()));
+              }
+
+            };
+            return true;
+          }
+          return false;
+        }
+
+        public Object next() {
+          if ((next == null) && !hasNext()) {
+            throw new NoSuchElementException();
+          }
+          Entry e = next;
+          next = null;
+          return e;
+        }
+
+        public void remove() {
+          it.remove();
+        }
+
+      };
+    }
+  }
+}

+ 137 - 0
src/main/java/de/mcs/utils/StartProcess.java

@@ -0,0 +1,137 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Starting a process like java.
+ * 
+ * @author w.klaas
+ * 
+ */
+public final class StartProcess {
+
+  private static List<Process> processes = new ArrayList<>();
+
+  // in case of jvm shutdown kill all processes gracefully
+  static {
+    Runtime.getRuntime().addShutdownHook(new Thread() {
+      @Override
+      public void run() {
+        killAll();
+      }
+    });
+  }
+
+  /** prevent instancing. */
+  private StartProcess() {
+
+  }
+
+  /**
+   * Starting java with a command list and optionally wait for program ending.
+   * 
+   * @param command
+   *          list of all needed parameters
+   * @param wait
+   *          wait for program ending
+   * @param workingDir
+   *          where is the working dir for this vm.
+   * @return the exit code of the process
+   */
+  public static int startJava(final List<String> command, final boolean wait, final String workingDir) {
+    Runtime rt = Runtime.getRuntime();
+    File workingDirFile = new File(workingDir);
+    int result = 0;
+    try {
+      Process p = rt.exec((String[]) command.toArray(new String[command.size()]), null, workingDirFile);
+      processes.add(p);
+      // create thread for reading inputStream (process' stdout)
+      StreamReaderThread outThread = new StreamReaderThread(p.getInputStream(), System.out);
+      // create thread for reading errorStream (process' stderr)
+      StreamReaderThread errThread = new StreamReaderThread(p.getErrorStream(), System.err);
+      // start both threads
+      outThread.start();
+      errThread.start();
+      // wait for process to end
+      if (wait) {
+        result = p.waitFor();
+        // finish reading whatever's left in the buffers
+        outThread.join();
+        errThread.join();
+
+        if (result != 0) {
+          System.err.println("Process " + command.get(0) + " returned non-zero value:" + result);
+        } else {
+          System.out.println("Process " + command.get(0) + " executed successfully");
+        }
+      }
+    } catch (IOException e) {
+      e.printStackTrace();
+      // } catch (InterruptedException e) {
+      // e.printStackTrace();
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+    cleanUp();
+    return result;
+  }
+
+  public static void cleanUp() {
+    for (Iterator<Process> iterator = processes.iterator(); iterator.hasNext();) {
+      Process process = iterator.next();
+      if (!process.isAlive()) {
+        iterator.remove();
+      }
+    }
+  }
+
+  public static void killAll() {
+    for (Iterator<Process> iterator = processes.iterator(); iterator.hasNext();) {
+      Process process = iterator.next();
+      if (process.isAlive()) {
+        process.destroy();
+      }
+      iterator.remove();
+    }
+  }
+
+  /**
+   * changing file permissions.
+   * 
+   * @param file
+   *          which file to change.
+   * @return result
+   */
+  public static int changeFilePermissions(final File file) {
+    int result = 0;
+    if (OSInformations.isMACOSX()) {
+      List<String> command = new ArrayList<String>();
+      command.add("chmod");
+      command.add("755");
+      command.add(file.getAbsolutePath());
+      result = startJava(command, true, file.getParent());
+      // do nothing for other systems at the moment
+    }
+    return result;
+  }
+}

+ 174 - 0
src/main/java/de/mcs/utils/StreamHelper.java

@@ -0,0 +1,174 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 23.04.2005 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.util.Arrays;
+
+/**
+ * This class contains some helper emthode for streaming operations.
+ * 
+ * @author W.Klaas
+ */
+public final class StreamHelper {
+
+  /** size of the internal buffer. */
+  private static final int BUFFER_SIZE = 32768;
+
+  /** no construction . */
+  private StreamHelper() {
+  }
+
+  /**
+   * copy all data from in to out.
+   * 
+   * @param out
+   *          outputstream to copy to
+   * @param in
+   *          input stream to copy from
+   * @return the bytes written
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static long copyStream(final InputStream in, final OutputStream out) throws IOException {
+    long written = 0;
+    byte[] buffer = new byte[BUFFER_SIZE];
+    int k = 0;
+    do {
+      k = in.read(buffer);
+      if (k > 0) {
+        out.write(buffer, 0, k);
+        written += k;
+      }
+    } while (k >= 0);
+    return written;
+  }
+
+  /**
+   * copy all data from in to out.
+   * 
+   * @param out
+   *          outputstream to copy to
+   * @param in
+   *          input stream to copy from
+   * @param bufSize
+   *          the size of the buffer to use.
+   * @return the bytes written
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static long copyStream(final InputStream in, final OutputStream out, final int bufSize) throws IOException {
+    long written = 0;
+    byte[] buffer = new byte[bufSize];
+    int k = 0;
+    do {
+      k = in.read(buffer);
+      if (k > 0) {
+        out.write(buffer, 0, k);
+        written += k;
+      }
+    } while (k >= 0);
+    return written;
+  }
+
+  /**
+   * compares to stream
+   * 
+   * @param in
+   *          input stream source
+   * @param in2
+   *          input stream to compare
+   * @return boolean true if the compared streams are identically
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static boolean equalStreams(final InputStream in, final InputStream in2) throws IOException {
+    boolean ok = true;
+    byte[] buffer = new byte[BUFFER_SIZE];
+    byte[] buffer2 = new byte[BUFFER_SIZE];
+    int k = 0;
+    int k2 = 0;
+    do {
+      k = in.read(buffer);
+      k2 = in2.read(buffer2);
+      ok = ok && (k == k2);
+      if (k > 0) {
+        ok = ok && Arrays.equals(buffer, buffer2);
+      }
+    } while (k >= 0);
+    return ok;
+  }
+
+  /**
+   * printing the file to System.out.
+   * 
+   * @param rcPropFile
+   *          file to print
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static void printOutFile(final File rcPropFile) throws IOException {
+    FileInputStream fileInputStream = new FileInputStream(rcPropFile);
+    Reader reader = new InputStreamReader(fileInputStream);
+    BufferedReader bufferedReader = new BufferedReader(reader);
+    try {
+      String line = null;
+      do {
+        line = bufferedReader.readLine();
+        System.out.println(line);
+      } while (null != line);
+    } finally {
+      bufferedReader.close();
+    }
+  }
+
+  /**
+   * Convert input stream to string.
+   *
+   * @param inputStream
+   *          the input stream
+   * @return the string
+   * @throws IOException
+   *           Signals that an I/O exception has occurred.
+   */
+  public static String convertInputStreamToString(InputStream inputStream) throws IOException {
+    if (inputStream != null) {
+      StringBuilder stringBuilder = new StringBuilder();
+      String line;
+
+      try {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
+        while ((line = reader.readLine()) != null) {
+          stringBuilder.append(line).append("\n");
+        }
+      } finally {
+        inputStream.close();
+      }
+
+      return stringBuilder.toString();
+    } else {
+      return null;
+    }
+  }
+}

+ 65 - 0
src/main/java/de/mcs/utils/StreamReaderThread.java

@@ -0,0 +1,65 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+
+/**
+ * A threaded stream reader needed for starting external processes.
+ * 
+ * @author w.klaas
+ */
+public class StreamReaderThread extends Thread {
+    /** where to put the results to. */
+    private OutputStream mOut;
+
+    /** where to get the result from. */
+    private InputStreamReader mIn;
+
+    /**
+     * constructor with in and out Streams.
+     * 
+     * @param in
+     *            inputstream
+     * @param out
+     *            Printstream for output
+     */
+    public StreamReaderThread(final InputStream in, final OutputStream out) {
+        mOut = out;
+        mIn = new InputStreamReader(in);
+    }
+
+    /**
+     * Threaded run. Now all data will be copied from in to out.
+     */
+    public final void run() {
+        int ch;
+        try {
+            while (-1 != (ch = mIn.read())) {
+                mOut.write((char) ch);
+            }
+        } catch (Exception e) {
+            try {
+                mOut.write(("\nRead error:" + e.getMessage()).getBytes());
+            } catch (IOException e1) {
+            }
+        }
+    }
+}

+ 947 - 0
src/main/java/de/mcs/utils/StringFormat.java

@@ -0,0 +1,947 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implements missing String format functions as a function library.
+ * 
+ * @version $Revision: 1.1 $
+ */
+public final class StringFormat {
+
+  /** Der MessageDigest. */
+  private static MessageDigest standardMd;
+  private static MessageDigest shaMd;
+
+  static {
+    try {
+      standardMd = MessageDigest.getInstance("MD5");
+      shaMd = MessageDigest.getInstance("SHA-256");
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /** i don't know. */
+  private static final String SPECIALSAVECHARS = "\\";
+
+  /** Die hexadezimalen Ziffern. */
+  private static final char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
+      'F' };
+
+  /** und nochmal als ein String. */
+  private static final String HEX_STRING = "0123456789ABCDEF";
+
+  /**
+   * Hide the Constructor.
+   */
+  private StringFormat() {
+  }
+
+  /**
+   * Converts a byte to hex digit and writes to the supplied buffer.
+   * 
+   * @param b
+   *          byte to convert
+   * @param buf
+   *          StringBuffer to append to
+   */
+  public static void byte2hex(final byte b, final StringBuffer buf) {
+    int high = ((b & 0xf0) >> 4);
+    int low = (b & 0x0f);
+    buf.append(HEX_CHARS[high]);
+    buf.append(HEX_CHARS[low]);
+  }
+
+  /**
+   * Converts a byte array to hex string.
+   * 
+   * @param block
+   *          bytearray to convert
+   * @return String Hexstring representing the block, constisting only from
+   *         uppercase letters [0-9A-F]*
+   */
+  static final char[] HEX_CHAR_ARRAY = "0123456789ABCDEF".toCharArray();
+
+  public static String toHexString(final byte[] block) {
+    StringBuilder sb = new StringBuilder(block.length * 2);
+    for (byte b : block) {
+      sb.append(HEX_CHAR_ARRAY[(b & 0xF0) >> 4]);
+      sb.append(HEX_CHAR_ARRAY[b & 0x0F]);
+    }
+    return sb.toString();
+  }
+
+  /**
+   * Converts the given hex string to a byte array. The hex string is
+   * interpreted as a sequence of hex digit pairs in high, low order. For odd
+   * length strings the last digit is interpreted as low value.
+   * 
+   * @param str
+   *          The hex string to convert to a byte array.
+   * @return A byte array containing the byte values of the hex string. throws
+   *         NumberFormatException if the given string isn't a hex string
+   */
+  public static byte[] fromHexString(final String str) {
+    String hexStr = str.toUpperCase();
+    final int len = hexStr.length();
+    final int l = len / 2;
+    byte[] bytes = new byte[l + (len % 2)];
+    int i, n;
+    for (i = 0, n = 0; i + 1 < len; i += 2, n++) {
+
+      int low = HEX_STRING.indexOf(hexStr.charAt(i + 1));
+      int high = HEX_STRING.indexOf(hexStr.charAt(i));
+      if (-1 == low || -1 == high) {
+        throw new NumberFormatException(hexStr);
+      }
+      bytes[n] = (byte) ((high << 4) | low);
+    }
+    if (len % 2 > 0) {
+      int low = HEX_STRING.indexOf(hexStr.charAt(i));
+      if (-1 == low) {
+        throw new NumberFormatException(hexStr);
+      }
+      bytes[n] = (byte) low;
+    }
+    return bytes;
+  }
+
+  /**
+   * Converts a string to its int equivalent.
+   * 
+   * @param strNum
+   *          The string that should be converted
+   * @return int The int version of the string value throws
+   *         NumberFormatException if the given String isn't a textual
+   *         representation of an int value.
+   */
+  public static int strToInt(final String strNum) {
+    return Integer.parseInt(strNum);
+  }
+
+  /**
+   * Returns a each 2 characters separated PathString from a MD5 HexString The
+   * used separator is System.getProperty("file.separator").
+   * 
+   * @param string
+   *          der String der gewandelt werden soll
+   * @param directoryDepth
+   *          Die Anzahl der Unterverzeichisse
+   * @return String
+   */
+  public static String getPathFromString(final String string, final int directoryDepth) {
+    int maxdirectoryDepth = (string.length() / 2);
+    StringBuffer buf = new StringBuffer();
+    if ((directoryDepth <= maxdirectoryDepth) && (directoryDepth >= 0)) {
+      String filedelim = System.getProperty("file.separator");
+      // Path
+      for (int i = 0; i < (directoryDepth * 2); i++) {
+        // File-Delimiter?
+        if ((i > 0) && ((i % 2) == 0)) {
+          buf.append(filedelim);
+        }
+        buf.append(string.charAt(i));
+      }
+      buf.append(filedelim);
+    }
+    return buf.toString();
+  }
+
+  /**
+   * Format the String representation of the given long to the given length and
+   * fills up at the beginning with the given fill value. l=654, length=5,
+   * fill='0' will lead to '00654'. If the result of l is longer than length,
+   * the full length of l is return
+   * 
+   * @param l
+   *          the long to be converted
+   * @param length
+   *          the length of the result
+   * @param fill
+   *          the fill value of the result
+   * @return the formatted string
+   */
+  public static String formatLong(final long l, final int length, final char fill) {
+    String x = String.valueOf(l);
+    if (x.length() < length) {
+      StringBuffer z = new StringBuffer();
+      for (int i = 0, s = length - x.length(); i < s; i++) {
+        z.append(fill);
+      }
+      z.append(x);
+      return z.toString();
+    } else {
+      return x;
+    }
+  }
+
+  /**
+   * Format the String representation of the given long to the given length and
+   * fills up at the beginning with the given fill value. l=654, length=5,
+   * fill='0' will lead to '00654'. If the result of l is longer than length,
+   * the full length of l is return
+   * 
+   * @param x
+   *          the long to be converted
+   * @param length
+   *          the length of the result
+   * @param fill
+   *          the fill value of the result
+   * @return the formatted string
+   */
+  public static String formatStringLeading(final String x, final int length, final char fill) {
+
+    if (x.length() < length) {
+      StringBuffer z = new StringBuffer();
+      for (int i = 0, s = length - x.length(); i < s; i++) {
+        z.append(fill);
+      }
+      z.append(x);
+      return z.toString();
+    } else {
+      if (x.length() == length) {
+        return x;
+      } else {
+        return x.substring(0, length + 1);
+      }
+    }
+  }
+
+  /**
+   * Format the String representation of the given long to the given length and
+   * fills up at the beginning with the given fill value. l=654, length=5,
+   * fill='0' will lead to '00654'. If the result of l is longer than length,
+   * the full length of l is return
+   * 
+   * @param x
+   *          the long to be converted
+   * @param length
+   *          the length of the result
+   * @param fill
+   *          the fill value of the result
+   * @return the formatted string
+   */
+  public static String formatString(final String x, final int length, final char fill) {
+
+    if (x.length() < length) {
+      StringBuffer z = new StringBuffer(x);
+      for (int i = 0, s = length - x.length(); i < s; i++) {
+        z.append(fill);
+      }
+      return z.toString();
+    } else {
+      if (x.length() == length) {
+        return x;
+      } else {
+        return x.substring(0, length + 1);
+      }
+    }
+  }
+
+  /**
+   * Method md5String.
+   * 
+   * @param s
+   *          String
+   * @return String
+   */
+  public static String md5String(final String s) {
+    // Build MD5-hashcode
+    MessageDigest md;
+    try {
+      md = (MessageDigest) standardMd.clone();
+      md.update(s.getBytes());
+      byte[] digest = md.digest();
+
+      return StringFormat.toHexString(digest);
+
+    } catch (CloneNotSupportedException e) {
+      // ignore, because cloning works
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /**
+   * Method md5String.
+   * 
+   * @param s
+   *          String
+   * @return String
+   */
+  public static String shaString(final String s) {
+    // Build SHA-hashcode
+    try {
+      MessageDigest md = (MessageDigest) shaMd.clone();
+      md.update(s.getBytes());
+      byte[] digest = md.digest();
+
+      return StringFormat.toHexString(digest);
+
+    } catch (CloneNotSupportedException e) {
+      // ignore, because cloning works
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /**
+   * Creates a well formed XML string.
+   * 
+   * @param g
+   *          original string
+   * @return a well formed string
+   */
+  public static String wellForm(final String g) {
+    StringBuffer newString = new StringBuffer();
+    for (int i = 0, s = g.length(); i < s; i++) {
+      char t = g.charAt(i);
+      switch (t) {
+      case '&':
+        newString.append("&amp;");
+        break;
+      case '>':
+        newString.append("&gt;");
+        break;
+      case '<':
+        newString.append("&lt;");
+        break;
+      case '"':
+        newString.append("&quot;");
+        break;
+      case '\'':
+        newString.append("&apos;");
+        break;
+      default:
+        newString.append(t);
+      }
+    }
+    return newString.toString();
+  }
+
+  /**
+   * Creates a String that contains all element of the given collection,
+   * seperated by the given seperator.
+   * 
+   * @param col
+   *          a collection of objects
+   * @param sep
+   *          the seperator
+   * @return result String
+   */
+  public static String getList(final Collection<Object> col, final char sep) {
+    StringBuffer buf = new StringBuffer();
+    boolean first = true;
+    for (Iterator<Object> iter = col.iterator(); iter.hasNext();) {
+      Object element = iter.next();
+      if (!first) {
+        buf.append(sep);
+      }
+      first = false;
+      if (element.toString().indexOf(sep) != -1) {
+        buf.append('"');
+        buf.append(element.toString());
+        buf.append('"');
+      } else {
+        buf.append(element.toString());
+      }
+
+    }
+    return buf.toString();
+
+  }
+
+  /**
+   * Parses the given String and returns a collection of String elements. If the
+   * separator must be part of the String, the single string element must be
+   * included in '"'
+   * 
+   * @param col
+   *          a string with separated strings
+   * @param sep
+   *          a seperator
+   * @return a collection of strings
+   */
+  public static List<String> parseList(final String col, final char sep) {
+    ArrayList<String> q = new ArrayList<String>();
+    StringBuffer element = new StringBuffer();
+    boolean ignore = false;
+    for (int i = 0; i < col.length(); i++) {
+      char v = col.charAt(i);
+      if (v == sep) {
+        if (!ignore) {
+          q.add(element.toString());
+          element = new StringBuffer();
+        } else {
+          element.append(v);
+        }
+      } else {
+        if (v == '"') {
+          if (element.length() == 0) {
+            ignore = true;
+          } else {
+            if (ignore) {
+              ignore = false;
+            } else {
+              element.append(v);
+            }
+          }
+        } else {
+          element.append(v);
+        }
+      }
+    }
+    if (element.length() != 0) {
+      q.add(element.toString());
+    }
+
+    return q;
+  }
+
+  /**
+   * Reads a string and searches for every occurence of one of the characters in
+   * <code>chars</code>. If the character occurs in <code>chars</code> it will
+   * be added to the result. If none of the characters in <code>chars</code>
+   * occurs in <code>source</code>, the method will return <code>null</code>.
+   * 
+   * @param source
+   *          the source to check
+   * @param chars
+   *          a string that contains every character to check for.
+   * @return the result of occurences of characters from <code>chars</code> in
+   *         <code>source</code> or <code>null</code>.
+   */
+  public static String containsChars(final String source, final String chars) {
+    StringBuffer errorChars = new StringBuffer();
+    for (int i = 0, length = source.length(); i < length; i++) {
+      char c = source.charAt(i);
+      if (chars.indexOf(c) != -1) {
+        if (errorChars.toString().indexOf(c) == -1) {
+          errorChars.append(c);
+        }
+      }
+    }
+    if (errorChars.length() > 0) {
+      return errorChars.toString();
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * Anzahl derr zeichen z�hlen.
+   * 
+   * @param searchString
+   *          the string to count chars
+   * @param character
+   *          the character to count
+   * @return the count of chars
+   */
+  public static int countsChars(final String searchString, final char character) {
+    int count = 0;
+    for (int i = 0, s = searchString.length(); i < s; i++) {
+      if (searchString.charAt(i) == character) {
+        count++;
+      }
+    }
+    return count;
+  }
+
+  /**
+   * @param r
+   *          a colletion that contains strings
+   * @return a String array
+   */
+  public static String[] fromCollection(final Collection<Object> r) {
+    String[] values = new String[r.size()];
+    int i = 0;
+    for (Iterator<Object> iter = r.iterator(); iter.hasNext();) {
+      String element = iter.next().toString();
+      values[i++] = element;
+    }
+    return values;
+  }
+
+  /**
+   * Removes white spaces (&gt;=ascii 32) from a string and all spaces at the
+   * end.
+   * 
+   * @param s
+   *          the input string
+   * @return a string without whitespaces
+   */
+  public static String filterWhiteSpace(final String s) {
+    StringBuffer result = new StringBuffer();
+    String sResult = "";
+    for (int i = 0; i < s.length(); i++) {
+      char x = s.charAt(i);
+      if (x > 31) {
+        result.append(x);
+      } else {
+        result.append(' ');
+      }
+    }
+    sResult = result.toString();
+    while (sResult.endsWith(" ")) {
+      sResult = sResult.substring(0, sResult.length() - 1);
+    }
+    return sResult;
+  }
+
+  /**
+   * Method replaceString.
+   * 
+   * @param newString
+   *          String
+   * @param source
+   *          String
+   * @param searchStr
+   *          String
+   * @return String
+   */
+  public static String replaceString(final String newString, final String source, final String searchStr) {
+    int position;
+    String sResult = source;
+    while ((position = source.indexOf(searchStr)) != -1) {
+      String start;
+      String end;
+      if (position > 0) {
+        start = source.substring(0, position);
+      } else {
+        start = "";
+      }
+      if (position + searchStr.length() - 1 < source.length() - 1) {
+        end = source.substring(position + searchStr.length(), source.length());
+      } else {
+        end = "";
+      }
+      sResult = start + newString + end;
+    }
+    return sResult;
+  }
+
+  /**
+   * Returns the value of a property. The given collection should contain
+   * strings with "name=value" entries. The method looks for the name and
+   * returns the value, else returns the default value
+   * 
+   * @param property
+   *          ???
+   * @param defaultValue
+   *          ???
+   * @param col
+   *          ???
+   * @return the value of the property or the default
+   */
+  public static String getProperty(final String property, final String defaultValue, final Collection<String> col) {
+    String value = defaultValue;
+    for (Iterator<String> iter = col.iterator(); iter.hasNext();) {
+      String element = iter.next();
+      int i = element.indexOf('=');
+      if (i != -1) {
+        String prop = element.substring(0, i).trim();
+        if (prop.equalsIgnoreCase(property)) {
+          value = element.substring(i + 1).trim();
+          if (value.startsWith("\"") && value.endsWith("\"")) {
+            value = value.substring(1, value.length() - 1);
+          }
+          break;
+        }
+      }
+    }
+    return value;
+  }
+
+  /**
+   * Gets a Map of properties from a collection that contains strings like
+   * "prop=value".
+   * 
+   * @param col
+   *          the properties
+   * @return a map
+   */
+  public static Map<String, String> getProperties(final Collection<String> col) {
+    HashMap<String, String> props = new HashMap<String, String>();
+    for (Iterator<String> iter = col.iterator(); iter.hasNext();) {
+      String element = (String) iter.next();
+      String prop;
+      String value;
+      int i = element.indexOf('=');
+      if (i != -1) {
+        prop = element.substring(0, i).trim();
+        value = element.substring(i + 1);
+        props.put(prop, value);
+      }
+    }
+    return props;
+  }
+
+  /**
+   * Creates a property string ( a list of comma separated key=value items) from
+   * a HashMap.
+   * 
+   * @param props
+   *          the properties
+   * @return a string that contains the key=value list
+   */
+  public static String getPropertyString(final Map<String, String> props) {
+    StringBuffer result = new StringBuffer();
+    for (Iterator<String> iter = props.keySet().iterator(); iter.hasNext();) {
+      String element = iter.next();
+      String value = element + "=" + props.get(element);
+      if (value.indexOf(",") != -1) {
+        value = '"' + value + '"';
+      }
+      if (result.length() > 0) {
+        result.append(',');
+      }
+      result.append(value);
+    }
+    return result.toString();
+  }
+
+  /**
+   * Sets a property in a property string list.
+   * 
+   * @param property
+   *          the name of the property
+   * @param value
+   *          the value
+   * @param propertyString
+   *          the property string list
+   * @return the result
+   */
+  public static String setProperty(final String property, final String value, final String propertyString) {
+    Map<String, String> m = getProperties(StringFormat.parseList(propertyString, ','));
+    m.put(property, value);
+    return getPropertyString(m);
+  }
+
+  /**
+   * Holt aus einer Stringliste (mit CRLF getrennt) eine Collection von Strings.
+   * 
+   * @param input
+   *          EingangsString
+   * @return Collection
+   */
+  public static Collection<String> getMultiLineString(final String input) {
+    ArrayList<String> lines = new ArrayList<String>();
+    StringBuffer actual = new StringBuffer();
+    for (int i = 0, length = input.length(); i < length; i++) {
+
+      char ch = input.charAt(i);
+      if (ch == 0x0a || ch == 0x0d) {
+        if (actual.length() != 0) {
+          lines.add(actual.toString());
+        }
+        actual = new StringBuffer();
+      } else {
+        actual.append(ch);
+      }
+    }
+    if (actual.length() != 0) {
+      lines.add(actual.toString());
+    }
+    return lines;
+  }
+
+  /**
+   * filters a string from the given characters. Example: filterString
+   * ("{blabla}", " {", "}") returns blabla
+   * 
+   * @param input
+   *          input String
+   * @param left
+   *          linke Begrenzung
+   * @param right
+   *          rechte Begrenzung
+   * @return the_filtered_string
+   */
+  public static String filterString(final String input, final char left, final char right) {
+    String sReturn = input;
+    int iLeft = input.indexOf(left);
+    if (iLeft > -1) {
+      sReturn = input.substring(iLeft + 1);
+    }
+    int iRight = sReturn.indexOf(right);
+    if (iRight > -1) {
+      sReturn = sReturn.substring(0, iRight);
+    }
+    return sReturn;
+  }
+
+  /**
+   * filters a string from the given characters. Example: filterString
+   * ("'blabla'", " '") returns blabla
+   * 
+   * @param input
+   *          zu filternder String
+   * @param filterchar
+   *          begrenzungszeichen
+   * @return the_filtered_string
+   */
+
+  public static String filterString(final String input, final char filterchar) {
+    String sReturn = input;
+
+    while (sReturn.startsWith("" + filterchar)) {
+      sReturn = sReturn.substring(1);
+    }
+    while (sReturn.endsWith("" + filterchar)) {
+      sReturn = sReturn.substring(0, sReturn.length() - 1);
+    }
+    return sReturn;
+  }
+
+  /**
+   * rightrim.
+   * 
+   * @param s
+   *          inputstring
+   * @return string
+   */
+  public static String rtrim(final String s) {
+    int firstx = 0;
+    for (int i = s.length() - 1; i >= 0; i--) {
+      if (s.charAt(i) > 32) {
+        break;
+      } else {
+        firstx = i;
+
+      }
+    }
+    if (firstx == 0) {
+      return "";
+    } else {
+      return s.substring(0, firstx);
+    }
+  }
+
+  /**
+   * Converts some special characters (tab, CR, LF, 0 etc) from java escape
+   * sequences to characters.
+   * 
+   * @param s
+   *          the string to convert.
+   * @return the converted string
+   */
+  public static String convertEscapes(final String s) {
+    StringBuffer r = new StringBuffer();
+    StringBuffer hexBuffer = null;
+    boolean code = false;
+    int hexCount = 0;
+    for (int i = 0, length = s.length(); i < length; i++) {
+      char c = s.charAt(i);
+      if (c == '\\') {
+        code = true;
+      } else {
+        if (code) {
+          switch (c) {
+          case '0':
+            hexBuffer = new StringBuffer();
+            hexCount = -1;
+            code = false;
+            break;
+          case 'r':
+            r.append('\r');
+            code = false;
+            break;
+          case 'f':
+            r.append('\f');
+            code = false;
+            break;
+          case 'n':
+            r.append('\n');
+            code = false;
+            break;
+          case 't':
+            r.append('\t');
+            code = false;
+            break;
+          case '\\':
+            r.append('\\');
+            code = false;
+            break;
+          default:
+          }
+        } else {
+          if (hexCount != 0) {
+            if (hexCount == -1) {
+              if (c == 'x') {
+                hexCount = 2;
+              } else if (c == 'u') {
+                hexCount = 4;
+              } else {
+                hexCount = 0;
+              }
+            } else {
+              hexBuffer.append(c);
+              hexCount--;
+              if (hexCount == 0) {
+                r.append((char) Integer.parseInt(hexBuffer.toString(), 16));
+              }
+            }
+
+          } else {
+            r.append(c);
+          }
+        }
+      }
+    }
+    return r.toString();
+
+  }
+
+  /**
+   * converting a string so that it can be saved into a human readable file.
+   * 
+   * @param theString
+   *          the string to convert
+   * @param escapeSpace
+   *          spaces must be escaped.
+   * @return the converted String.
+   */
+  public static String saveConvert(final String theString, final boolean escapeSpace) {
+    int len = theString.length();
+    StringBuffer outBuffer = new StringBuffer(len * 2);
+
+    for (int x = 0; x < len; x++) {
+      char aChar = theString.charAt(x);
+      switch (aChar) {
+      case ' ':
+        if (x == 0 || escapeSpace) {
+          outBuffer.append('\\');
+        }
+
+        outBuffer.append(' ');
+        break;
+      case '\\':
+        outBuffer.append('\\');
+        outBuffer.append('\\');
+        break;
+      case '\t':
+        outBuffer.append('\\');
+        outBuffer.append('t');
+        break;
+      case '\n':
+        outBuffer.append('\\');
+        outBuffer.append('n');
+        break;
+      case '\r':
+        outBuffer.append('\\');
+        outBuffer.append('r');
+        break;
+      case '\f':
+        outBuffer.append('\\');
+        outBuffer.append('f');
+        break;
+      default:
+        // if ((aChar < 0x0020) || (aChar > 0x007e)) {
+        // outBuffer.append('\\');
+        // outBuffer.append('u');
+        // outBuffer.append(toHex((aChar >> 12) & 0xF));
+        // outBuffer.append(toHex((aChar >> 8) & 0xF));
+        // outBuffer.append(toHex((aChar >> 4) & 0xF));
+        // outBuffer.append(toHex(aChar & 0xF));
+        // } else {
+        if (SPECIALSAVECHARS.indexOf(aChar) != -1) {
+          outBuffer.append('\\');
+        }
+        outBuffer.append(aChar);
+        // }
+      }
+    }
+    return outBuffer.toString();
+  }
+
+  /**
+   * Convert a nibble to a hex character.
+   * 
+   * @param nibble
+   *          the nibble to convert.
+   * @return char
+   */
+  @SuppressWarnings("unused")
+  private static char toHex(final int nibble) {
+    return HEXDIGIT[(nibble & 0xF)];
+  }
+
+  /** A table of hex digits. */
+  private static final char[] HEXDIGIT = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
+      'F' };
+
+  /**
+   * getting a stack trace as a string like in exception.printStackTrace.
+   * 
+   * @param cause
+   *          the exception to convert.
+   * @return String
+   */
+  public static String getStackTrace(final Throwable cause) {
+    StackTraceElement[] elements = cause.getStackTrace();
+    StringBuffer buffer = new StringBuffer(1024);
+    buffer.append(cause);
+    for (int i = 0; i < elements.length; i++) {
+      StackTraceElement element = elements[i];
+      buffer.append("\r\n\tat ");
+      buffer.append(element.toString());
+    }
+    return buffer.toString();
+  }
+
+  public static String left(final String string, final int pos) {
+    return string.substring(0, pos);
+  }
+
+  public static String right(final String string, final int pos) {
+    return string.substring(pos);
+  }
+
+  public static String[] splitFirst(final String string, final String search) {
+    String[] result = new String[2];
+    int pos = string.indexOf(search);
+    if (pos == -1) {
+      result[0] = string;
+    } else {
+      result[0] = left(string, pos);
+      if (pos < string.length()) {
+        result[1] = right(string, pos + search.length());
+      }
+    }
+    return result;
+  }
+
+  public static boolean isUppercase(final String string) {
+    /* Now check if there are any characters that need to be changed. */
+    for (int index = 0; index < string.length(); index++) {
+      if (!Character.isUpperCase(string.codePointAt(index))) {
+        return false;
+      }
+    }
+    return true;
+  }
+}

+ 712 - 0
src/main/java/de/mcs/utils/StringUtils.java

@@ -0,0 +1,712 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.StringTokenizer;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+import de.mcs.utils.codecs.Base64;
+
+/**
+ * This class provides static util methods for String manaipulation that aren't
+ * part of the default JDK functionalities.
+ * 
+ * @author w.klaas
+ */
+public final class StringUtils {
+  /** SPCAE Character. */
+  private static final String SPACE = " ";
+
+  /**
+   * private constructor, to provide instancing.
+   */
+  private StringUtils() {
+  }
+
+  /**
+   * Replaces all the occurences of a substring found within a StringBuffer by a
+   * replacement string.
+   * 
+   * @param buffer
+   *          the StringBuffer in where the replace will take place
+   * @param find
+   *          the substring to find and replace
+   * @param replacement
+   *          the replacement string for all occurences of find
+   * @return the original StringBuffer where all the occurences of find are
+   *         replaced by replacement
+   */
+  public static StringBuffer replaceAll(final StringBuffer buffer, final String find, final String replacement) {
+    int bufidx = buffer.length() - 1;
+    int offset = find.length();
+    while (bufidx > -1) {
+      int findidx = offset - 1;
+      while (findidx > -1) {
+        if (bufidx == -1) {
+          // Done
+          return buffer;
+        }
+        if (buffer.charAt(bufidx) == find.charAt(findidx)) {
+          findidx--; // Look for next char
+          bufidx--;
+        } else {
+          findidx = offset - 1; // Start looking again
+          bufidx--;
+          if (bufidx == -1) {
+            // Done
+            return buffer;
+          }
+          continue;
+        }
+      }
+      buffer.replace(bufidx + 1, bufidx + 1 + offset, replacement);
+      // start looking again
+    }
+    // No more matches
+    return buffer;
+
+  }
+
+  /**
+   * convinient methode for converting. seperator will be ',' delimiter will be
+   * '"'.
+   * 
+   * @param list
+   *          String The array of strings input.
+   * @return String A string containing tokens separated by seperator.
+   */
+  public static String listToCSVString(List<String> list) {
+    return listToCSVString(list, false);
+  }
+
+  public static String listToCSVString(List<String> list, boolean forceDelimiter) {
+    // TODO Auto-generated method stub
+    String[] array = new String[list.size()];
+    for (int i = 0; i < array.length; i++) {
+      array[i] = list.get(i).toString();
+    }
+    return arrayToCSVString(array, ',', '"', forceDelimiter);
+  }
+
+  /**
+   * convinient methode for converting. seperator will be ',' delimiter will be
+   * '"'.
+   * 
+   * @param array
+   *          The array of strings input.
+   * @return String A string containing tokens separated by seperator.
+   */
+  public static String arrayToCSVString(final String[] array) {
+    return arrayToCSVString(array, ',', '"');
+  }
+
+  public static String arrayToCSVString(final String[] array, final char localseparators, final char localdelimiter) {
+    return arrayToCSVString(array, localseparators, localdelimiter, false);
+  }
+
+  /**
+   * Takes an array of tokens and converts into separator-separated string.
+   * 
+   * @param array
+   *          The array of strings input.
+   * @param localseparators
+   *          The separator char.
+   * @param localdelimiter
+   *          The delimiter char.
+   * @param forceDelimiter
+   *          forces to delimit the texts, even if this is not necessary
+   * @return String A string containing tokens separated by separator.
+   */
+  public static String arrayToCSVString(final String[] array, final char localseparators, final char localdelimiter,
+      boolean forceDelimiter) {
+    String delimiter = Character.toString(localdelimiter);
+    String separators = Character.toString(localseparators);
+    StringBuffer sb = new StringBuffer("");
+    String empty = "";
+
+    if ((array == null) || (array.length == 0)) {
+      return empty;
+    }
+
+    if (delimiter == null) {
+      delimiter = "\"";
+    }
+
+    if (separators == null) {
+      separators = ",";
+    }
+
+    for (int ix = 0; ix < array.length; ix++) {
+      // if (array[ix] != null && !array[ix].equals("")) {
+      if (array[ix] != null) {
+        StringBuffer value = new StringBuffer(array[ix]);
+        if (value.indexOf(delimiter) >= 0) {
+          value = replaceAll(value, delimiter, delimiter + delimiter);
+        }
+        if (forceDelimiter || (array[ix].indexOf(separators) >= 0) || (array[ix].indexOf(delimiter) >= 0)
+            || array[ix].startsWith(" ")) {
+          sb.append(delimiter);
+          sb.append(value);
+          sb.append(delimiter);
+          sb.append(separators);
+        } else {
+          sb.append(value);
+          sb.append(separators);
+        }
+      }
+    }
+    String str = sb.toString();
+    if (!str.equals("")) {
+      str = str.substring(0, (str.length() - separators.length()));
+    }
+    return str;
+  }
+
+  /**
+   * Converts a delimited string into an array of string tokens. Separator is
+   * ',', delimiter is '"'
+   * 
+   * @param string
+   *          The 'separator' separated string.
+   * @return String[] A string array of the original tokens.
+   */
+  public static String[] csvStringToArray(final String string) {
+    return csvStringToArray(string, ',', '"');
+  }
+
+  /**
+   * Converts a delimited string into an array of string tokens.
+   * 
+   * @param str
+   *          The 'separator' separated string.
+   * @param localseparators
+   *          The separator char.
+   * @param localdelimiter
+   *          The delimiter char.
+   * @return String[] A string array of the original tokens.
+   */
+  public static String[] csvStringToArray(final String str, final char localseparators, final char localdelimiter) {
+    /**
+     * Parse comma-separated values (CSV), a common Windows file format. Sample
+     * input: "LU",86.25,"11/4/1998","2:19PM",+4.0625
+     * <p>
+     * Inner logic adapted from a C++ original that was Copyright (C) 1999
+     * Lucent Technologies Excerpted from 'The Practice of Programming' by Brian
+     * W. Kernighan and Rob Pike.
+     * <p>
+     * Included by permission of the http://tpop.awl.com/ web site, which says:
+     * "You may use this code for any purpose, as long as you leave the
+     * copyright notice and book citation attached." I have done so.
+     * 
+     * @author Brian W. Kernighan and Rob Pike (C++ original)
+     * @author Ian F. Darwin (translation into Java and removal of I/O)
+     * @author Ben Ballard (rewrote advQuoted to handle '""' and for
+     *         readability)
+     */
+    class CSV {
+
+      // public static final char DEFAULT_SEP = ',';
+
+      // public static final char DEFAULT_DELIM = '\"';
+
+      /** Construct a CSV parser, with the default separator (`,'). */
+      // private CSV() {
+      // this(DEFAULT_SEP, DEFAULT_DELIM);
+      // }
+
+      /**
+       * Construct a CSV parser with a given separator. Must be exactly the
+       * string that is the separator, not a list of separator characters!
+       * 
+       * @param delim
+       *          field delimiter
+       * @param sep
+       *          field separator
+       */
+      public CSV(final char sep, final char delim) {
+        fieldSep = sep;
+        fieldDelim = delim;
+      }
+
+      /** The fields in the current String */
+      ArrayList<String> list = new ArrayList<String>();
+
+      /** the separator char for this parser */
+      private char fieldSep;
+
+      /** the delimiter char for this parser */
+      private char fieldDelim;
+
+      /**
+       * parse: break the input String into fields
+       * 
+       * @param line
+       *          the line to parse
+       * @return java.util.Iterator containing each field from the original as a
+       *         String, in order.
+       */
+      public final Iterator<String> parse(final String line) {
+        StringBuffer sb = new StringBuffer();
+        list.clear(); // discard previous, if any
+        int i = 0;
+
+        if (line.length() == 0) {
+          // list.add(line);
+          return list.iterator();
+        }
+        do {
+          sb.setLength(0);
+          if (i < line.length() && line.charAt(i) == fieldDelim) {
+            i = advQuoted(line, sb, ++i); // skip quote
+          } else {
+            i = advPlain(line, sb, i);
+          }
+          list.add(sb.toString());
+          // Debug.println("csv", sb.toString());
+          i++;
+        } while (i < line.length());
+        return list.iterator();
+      }
+
+      /**
+       * advQuoted: quoted field; return index of next separator
+       * 
+       * @param s
+       *          String
+       * @param sb
+       *          StringBuffer
+       * @param i
+       *          int
+       * @return int
+       */
+      private int advQuoted(final String s, final StringBuffer sb, final int i) {
+        int j;
+        int len = s.length();
+        for (j = i; j < len; j++) {
+          if (s.charAt(j) == fieldDelim && j + 1 < len) {
+            if (s.charAt(j + 1) == fieldDelim) {
+              j++; // skip escape char
+            } else if (s.charAt(j + 1) == fieldSep) { // next
+              // delimeter
+              j++; // skip end quotes
+              break;
+            }
+          } else if (s.charAt(j) == fieldDelim && j + 1 == len) { // end
+            // quotes at end of line
+            break; // done
+          }
+          sb.append(s.charAt(j)); // regular character.
+        }
+        return j;
+      }
+
+      /**
+       * advPlain: unquoted field; return index of next separator
+       * 
+       * @param s
+       *          String
+       * @param sb
+       *          StringBuffer
+       * @param i
+       *          int
+       * @return int
+       */
+      private int advPlain(final String s, final StringBuffer sb, final int i) {
+        int j;
+
+        j = s.indexOf(fieldSep, i); // look for separator
+        // Debug.println("csv", "i = " + i + ", j = " + j);
+        if (j == -1) { // none found
+          sb.append(s.substring(i));
+          return s.length();
+        } else {
+          sb.append(s.substring(i, j));
+          return j;
+        }
+      }
+    }
+    CSV csv = new CSV(localseparators, localdelimiter);
+    csv.parse(str);
+    return (String[]) csv.list.toArray(new String[0]);
+  }
+
+  /**
+   * Remove a given set of characters from a String.
+   * 
+   * @param data
+   *          The input string to be cleansed of 'removeChars'.
+   * @param removeChars
+   *          The characters to be removed.
+   * @return String The new string cleansed of 'removeChars'.
+   */
+  public static String removeChars(final String data, final String removeChars) {
+    String temp = null;
+    StringBuffer out = new StringBuffer();
+    temp = data;
+
+    StringTokenizer st = new StringTokenizer(temp, removeChars);
+    while (st.hasMoreTokens()) {
+      String element = (String) st.nextElement();
+      out.append(element);
+    }
+    return out.toString();
+  }
+
+  /**
+   * Given a filename, strips the .extension.
+   * 
+   * @param filename
+   *          filename to strip
+   * @return String filename without .extension
+   */
+  public static String stripExtension(final String filename) {
+    int index = filename.lastIndexOf('.');
+    if (index > -1) {
+      return filename.substring(0, index);
+    }
+    return filename;
+  }
+
+  /**
+   * Performs variable substitution for a string. String is scanned for
+   * ${variable_name} and if one is found, it is replaced with corresponding
+   * value from the vars hashtable.
+   * 
+   * @param origString
+   *          unmodified string
+   * @param vars
+   *          Hashtable of replacement values
+   * @return modified string
+   */
+  public static String replaceVars(final String origString, final Map<String, String> vars) {
+
+    StringBuffer finalString = new StringBuffer();
+    int index = 0;
+    int i = 0;
+    String key = null;
+    String value = null;
+    while ((index = origString.indexOf("${", i)) > -1) {
+      key = origString.substring(index + 2, origString.indexOf("}", index + 3));
+      value = vars.get(key);
+      finalString.append(origString.substring(i, index));
+      if (value != null) {
+        finalString.append(value);
+      } else {
+        finalString.append("${" + key + "}");
+      }
+      i = index + 3 + key.length();
+    }
+    finalString.append(origString.substring(i));
+
+    return finalString.toString();
+  }
+
+  /**
+   * Check strSearchValue is in Array.
+   * 
+   * @param fieldProps
+   *          Array
+   * @param strSearchValue
+   *          SearchString
+   * @return integer Value of Index in Array
+   */
+  public static int isInArray(final String[] fieldProps, final String strSearchValue) {
+    for (int i = 0; i < fieldProps.length; i++) {
+      if (fieldProps[i].equals(strSearchValue)) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * Convert byte array to a mime coded string.
+   * 
+   * @param array
+   *          the array to convert
+   * @return mime coded String
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public static String byteArrayToMimeString(final byte[] array) throws IOException {
+    return Base64.encodeBytes(array);
+  }
+
+  /**
+   * Convert a mimecoded string to a array of byte.
+   * 
+   * @param string
+   *          the mime coded string
+   * @return an array of byte
+   * @throws Exception
+   *           if something goes wrong
+   */
+  public static byte[] mimeStringTobyteArray(final String string) throws Exception {
+    return Base64.decode(string);
+  }
+
+  /**
+   * building the md5 hash of an String.
+   * 
+   * @param s
+   *          String
+   * @return String
+   */
+  public static String md5String(final String s) {
+    // Build MD5-hashcode
+    MessageDigest md;
+    try {
+      md = MessageDigest.getInstance("MD5");
+      md.update(s.getBytes());
+      byte[] digest = md.digest();
+
+      return StringFormat.toHexString(digest);
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /**
+   * Method md5String.
+   * 
+   * @param s
+   *          String
+   * @return byte[[]
+   */
+  public static byte[] md5StringBytes(final String s) {
+    // Build MD5-hashcode
+    MessageDigest md;
+    try {
+      md = MessageDigest.getInstance("MD5");
+      md.update(s.getBytes());
+      byte[] digest = md.digest();
+
+      return digest;
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /**
+   * building the md5 hash of an String.
+   * 
+   * @param s
+   *          String
+   * @return String
+   */
+  public static String md5UTF8String(final String s) {
+    // Build MD5-hashcode
+    MessageDigest md;
+    try {
+      md = MessageDigest.getInstance("MD5");
+      md.update(s.getBytes("UTF-8"));
+      byte[] digest = md.digest();
+
+      return StringFormat.toHexString(digest);
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    } catch (UnsupportedEncodingException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /**
+   * xorArrays.
+   * 
+   * @param arg0
+   *          byte[]
+   * @param arg1
+   *          byte[]
+   * @return byte[]
+   */
+  public static byte[] xorArrays(final byte[] arg0, final byte[] arg1) {
+    byte[] xor = new byte[arg0.length];
+    int keylength = arg1.length;
+    for (int i = 0; i < xor.length; i++) {
+      xor[i] = (byte) (arg0[i] ^ arg1[i % keylength]);
+    }
+    return xor;
+  }
+
+  /**
+   * Getting the value formatted to the actual fieldsize.
+   * 
+   * @param aValue
+   *          value to format
+   * @param size
+   *          array index of this field
+   * @return String
+   */
+  public static String formatFieldData(final String aValue, final int size) {
+    StringBuffer value = new StringBuffer(aValue);
+    while (value.length() < size) {
+      value.append(SPACE);
+    }
+    if (value.length() > size) {
+      value = new StringBuffer(value.substring(0, size));
+    }
+    return value.toString();
+  }
+
+  /**
+   * converts a string array into a list of strings.
+   * 
+   * @param strings
+   *          string array to convert to
+   * @return a list.
+   */
+  public static List<String> toList(final String[] strings) {
+    List<String> list = new ArrayList<String>(strings.length);
+    for (int i = 0; i < strings.length; i++) {
+      list.add(strings[i]);
+    }
+    return list;
+  }
+
+  /**
+   * Converts a delimited string into a list of string tokens. Separator is ',',
+   * delimiter is '"'
+   * 
+   * @param string
+   *          The 'separator' separated string.
+   * @return String[] A string array of the original tokens.
+   */
+  public static List<String> csvStringToList(final String string) {
+    String[] strings = csvStringToArray(string, ',', '"');
+    return toList(strings);
+  }
+
+  /**
+   * Converts a delimited string into a list of string tokens. Separator and
+   * delimiter could be used.
+   * 
+   * @param string
+   *          The 'separator' separated string.
+   * @param localseparator
+   *          The separator char.
+   * @param localdelimiter
+   *          The delimiter char.
+   * @return String[] A string array of the original tokens.
+   */
+  public static List<String> csvStringToList(final String string, final char localseparator,
+      final char localdelimiter) {
+    String[] strings = csvStringToArray(string, localseparator, localdelimiter);
+    return toList(strings);
+  }
+
+  /**
+   * converting a csvString in a property map.
+   * 
+   * @param csvString
+   *          the string
+   * @param csvSeparator
+   *          the separator
+   * @param csvDelimiter
+   *          the delimiter
+   * @param propSeparator
+   *          the prop separator
+   * @return the Map String, String
+   */
+  public static Map<String, String> csvStringToPropMap(String csvString, char csvSeparator, char csvDelimiter,
+      char propSeparator) {
+    Map<String, String> map = new HashMap<String, String>();
+    String[] propStrings = csvStringToArray(csvString, csvSeparator, csvDelimiter);
+    for (String propString : propStrings) {
+      int pos = propString.indexOf(propSeparator);
+      if (pos >= 0) {
+        String key = propString.substring(0, pos);
+        String value = propString.substring(pos + 1);
+        map.put(key, value);
+      }
+    }
+    return map;
+  }
+
+  /**
+   * converting a property map in a csvString .
+   * 
+   * @param map
+   *          the map to convert
+   * @param csvSeparator
+   *          the seperator
+   * @param csvDelimiter
+   *          the delimiter
+   * @param propSeparator
+   *          the prop seperator
+   * @return String
+   */
+  public static String propMapToCsvString(Map<String, String> map, char csvSeparator, char csvDelimiter,
+      char propSeparator) {
+    StringBuilder b = new StringBuilder();
+    for (Iterator<Entry<String, String>> iterator = map.entrySet().iterator(); iterator.hasNext();) {
+      Entry<String, String> entry = iterator.next();
+      b.append(csvDelimiter);
+      b.append(entry.getKey());
+      b.append(propSeparator);
+      b.append(entry.getValue());
+      b.append(csvDelimiter);
+      if (iterator.hasNext()) {
+        b.append(csvSeparator);
+      }
+    }
+    return b.toString();
+  }
+
+  static byte[] raw = new byte[] { 'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' };
+
+  public static String encrypt(String value, byte[] key) {
+    try {
+      SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
+      Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
+      cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
+      byte[] encrypted = cipher.doFinal(value.getBytes());
+      return Base64.encodeBytes(encrypted);
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+    return null;
+  }
+
+  public static String decrypt(String encrypted, byte[] key) {
+    try {
+      SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
+      Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
+      cipher.init(Cipher.DECRYPT_MODE, skeySpec);
+      byte[] original = cipher.doFinal(Base64.decode(encrypted));
+      return new String(original);
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+    return null;
+  }
+}

+ 25 - 0
src/main/java/de/mcs/utils/SystemPathes.java

@@ -0,0 +1,25 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2011 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: SystemPathes.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 23.01.2011 Willie
+ */
+
+package de.mcs.utils;
+
+import java.io.File;
+
+/**
+ * @author Willie
+ * 
+ */
+public class SystemPathes {
+
+  public static final File getUserPath() {
+    String userHome = "user.home";
+    String path = System.getProperty(userHome);
+    return new File(path);
+  }
+}

+ 142 - 0
src/main/java/de/mcs/utils/ThreadPool.java

@@ -0,0 +1,142 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2006 by MCS
+ * -------------------------------------- Created on 11.04.2006 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.util.ArrayList;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * this class is intend to implement a thread pool with the possibility to start
+ * and stop all threads and to interact with some other parts of the program.
+ * 
+ * @author w.klaas
+ */
+public class ThreadPool extends Thread {
+
+    /** the thread pool. */
+    private ArrayList<WorkingThread> pool;
+
+    /** the working queue. */
+    private Queue<Runnable> queue;
+
+    /** maximal count of threads. */
+    private int maxCount;
+
+    /**
+     * instancing the thread pool. With max count of parallel threads.
+     * 
+     * @param maxcount
+     *            maximal count of threads
+     */
+    public ThreadPool(final int maxcount) {
+        this.maxCount = maxcount;
+        this.pool = new ArrayList<WorkingThread>(maxCount);
+        this.queue = new LinkedBlockingQueue<Runnable>(100);
+    }
+
+    /**
+     * Adding a new working part to the queue.
+     * 
+     * @param work
+     *            the runnable to start.
+     * @return <code>true</code> if the work could be added, otherwise
+     *         <code>false</code>
+     */
+    public final boolean addWorkingPart(final Runnable work) {
+        return queue.offer(work);
+    }
+
+    /**
+     * removing a working part from the queue.
+     * 
+     * @param work
+     *            the runnable to remove.
+     * @return <code>true</code> if the work could be removed, otherwise
+     *         <code>false</code>
+     */
+    public final boolean removeWorkingPart(final Runnable work) {
+        return queue.remove(work);
+    }
+
+    /**
+     * @see java.lang.Thread#run()
+     */
+    @Override
+    public final void run() {
+        while (!isInterrupted()) {
+            if (!queue.isEmpty()) {
+                Runnable run = queue.poll();
+                try {
+                    WorkingThread thread = getNextFreeThread();
+                    thread.setRunnable(run);
+                    thread.start();
+                } catch (InterruptedException e) {
+                    // e.printStackTrace();
+                }
+            } else {
+                try {
+                    sleep(100);
+                } catch (InterruptedException e) {
+                    // e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     * getting the next thread that should be startet.
+     * 
+     * @return WorkingThread
+     * @throws InterruptedException
+     *             if the process ahould be stoped.
+     */
+    private WorkingThread getNextFreeThread() throws InterruptedException {
+        // zun�chst mal schauen ob die maxcount nicht geändert worden ist und
+        // demnach dann die Anzahl der Working Threads anpassen
+        while (maxCount < pool.size()) {
+            // jetzt warten bis die Anzahl wieder stimmt.
+            WorkingThread thread = getFreeThread();
+            pool.remove(thread);
+            thread = null;
+        }
+        while (maxCount > pool.size()) {
+            WorkingThread thread = new WorkingThread();
+            pool.add(thread);
+        }
+        return getFreeThread();
+    }
+
+    /**
+     * getting the next thread that should be startet.
+     * 
+     * @return WorkingThread
+     * @throws InterruptedException
+     *             if the process ahould be stoped.
+     */
+    private WorkingThread getFreeThread() throws InterruptedException {
+        while (true) {
+            for (WorkingThread thread : pool) {
+                if (thread.isFree()) {
+                    thread.setBusy();
+                    return thread;
+                }
+            }
+            sleep(50);
+        }
+    }
+}

+ 165 - 0
src/main/java/de/mcs/utils/TimeHelper.java

@@ -0,0 +1,165 @@
+package de.mcs.utils;
+
+import java.sql.Date;
+
+/**
+ * Helper class for onverting time stamps
+ * 
+ * @author w.klaas
+ */
+public class TimeHelper {
+
+  /**
+   * Converting seconds into millis
+   * 
+   * @param value
+   *          seconds
+   * @return millis
+   */
+  public static long sec2millis(long value) {
+    return value * 1000;
+  }
+
+  /**
+   * Converting mintues into millis
+   * 
+   * @param value
+   *          minutes
+   * @return millis
+   */
+  public static long min2millis(long value) {
+    return value * 1000 * 60;
+  }
+
+  /**
+   * Converting hours into millis
+   * 
+   * @param value
+   *          hours
+   * @return millis
+   */
+  public static long hour2millis(long value) {
+    return value * 1000 * 60 * 60;
+  }
+
+  /**
+   * Converting millis into seconds (the rest will be ignored, no rounding)
+   * 
+   * @param value
+   *          millis
+   * @return seconds
+   */
+  public static long millis2sec(long value) {
+    return value / 1000;
+  }
+
+  /**
+   * Converting millis into minutes (the rest will be ignored, no rounding)
+   * 
+   * @param value
+   *          millis
+   * @return minutes
+   */
+  public static long millis2min(long value) {
+    return value / (1000 * 60);
+  }
+
+  /**
+   * Converting millis into hours (the rest will be ignored, no rounding)
+   * 
+   * @param value
+   *          millis
+   * @return hours
+   */
+  public static long millis2hour(long value) {
+    return value / (1000 * 60 * 60);
+  }
+
+  /**
+   * Converting minutes into seconds
+   * 
+   * @param value
+   *          minutes
+   * @return seconds
+   */
+  public static long min2sec(long value) {
+    return value * 60;
+  }
+
+  /**
+   * Converting hours into seconds
+   * 
+   * @param value
+   *          hours
+   * @return seconds
+   */
+  public static long hour2sec(long value) {
+    return value * 60 * 60;
+  }
+
+  /**
+   * Converting hours into minutes
+   * 
+   * @param value
+   *          hours
+   * @return minutes
+   */
+  public static long hour2min(long value) {
+    return value * 60;
+  }
+
+  /**
+   * Converting seconds into minutes (the rest will be ignored, no rounding)
+   * 
+   * @param value
+   *          seconds
+   * @return minutes
+   */
+  public static long sec2min(long value) {
+    return value / 60;
+  }
+
+  /**
+   * Converting seconds into hours (the rest will be ignored, no rounding)
+   * 
+   * @param value
+   *          seconds
+   * @return hours
+   */
+  public static long sec2hour(long value) {
+    return value / (60 * 60);
+  }
+
+  /**
+   * Converting minutes into hours (the rest will be ignored, no rounding)
+   * 
+   * @param value
+   *          minutes
+   * @return hours
+   */
+  public static long min2hour(long value) {
+    return value / 60;
+  }
+
+  /**
+   * Converting date into millis
+   * 
+   * @param value
+   *          date
+   * @return millis
+   */
+  public static long date2millis(Date value) {
+    return value.getTime();
+  }
+
+  /**
+   * Converting millis into date
+   * 
+   * @param value
+   *          millis
+   * @return date
+   */
+  public static Date date2millis(long value) {
+    return new Date(value);
+  }
+}

+ 44 - 0
src/main/java/de/mcs/utils/URLUtilities.java

@@ -0,0 +1,44 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2014 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: URLUtilities.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 16.02.2014 wklaa_000
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+package de.mcs.utils;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+/**
+ * @author wklaa_000
+ * 
+ */
+public class URLUtilities {
+
+  public static void openUrl(String url) throws IOException, URISyntaxException {
+    if (java.awt.Desktop.isDesktopSupported()) {
+      java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
+
+      if (desktop.isSupported(java.awt.Desktop.Action.BROWSE)) {
+        java.net.URI uri = new java.net.URI(url);
+        desktop.browse(uri);
+      }
+    }
+  }
+
+}

+ 358 - 0
src/main/java/de/mcs/utils/Version.java

@@ -0,0 +1,358 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+/**
+ * this is a littel class to help versioning. Version could be build from a
+ * string with the following syntax: major.minor.release.build
+ * 
+ * @author w.klaas
+ */
+public class Version {
+
+  public static final String SNAPSHOT_MARKER = "SNAPSHOT";
+
+  private static final String VERSION_STATE_MARKER = "-";
+
+  /** major version. */
+  private int major;
+
+  /** minor version. */
+  private int minor;
+
+  /** release version. */
+  private int release;
+
+  /** build version. */
+  private int build;
+
+  private String versionState;
+
+  /**
+   * constructor for empty version.
+   */
+  public Version() {
+    major = -1;
+    minor = -1;
+    release = -1;
+    build = -1;
+    versionState = null;
+  }
+
+  /**
+   * Constructor for creating a version object from an string.
+   * 
+   * @param version
+   *          string to convert
+   */
+  public Version(final String version) {
+    this();
+    parseString(version);
+  }
+
+  /**
+   * parsing a string into this version class.
+   * 
+   * @param version
+   *          string to parse
+   */
+  public final void parseString(final String version) {
+    String localVersion = version;
+    int posOfVersionStateMarker = localVersion.indexOf(VERSION_STATE_MARKER);
+    if (posOfVersionStateMarker >= 0) {
+      versionState = localVersion.substring(posOfVersionStateMarker + 1);
+      if (versionState != null) {
+        versionState = versionState.trim();
+      }
+      localVersion = localVersion.substring(0, posOfVersionStateMarker).trim();
+    }
+    String[] versionvalues = localVersion.split("\\.");
+    for (int i = 0; i < versionvalues.length; i++) {
+      String string = versionvalues[i].trim();
+      switch (i) {
+      case 0:
+        major = Integer.parseInt(string);
+        break;
+      case 1:
+        minor = Integer.parseInt(string);
+        break;
+      case 2:
+        release = Integer.parseInt(string);
+        break;
+      case 3:
+        build = Integer.parseInt(string);
+        break;
+      default:
+        break;
+      }
+    }
+  }
+
+  /**
+   * constructor from another version class.
+   * 
+   * @param version
+   *          version to use
+   */
+  public Version(final Version version) {
+    this();
+    this.major = version.major;
+    this.minor = version.minor;
+    this.release = version.release;
+    this.build = version.build;
+  }
+
+  /** increment the major field. */
+  public final void incrementMajor() {
+    major++;
+  }
+
+  /** increment the minor field. */
+  public final void incrementMinor() {
+    minor++;
+  }
+
+  /** incementing one the release field. */
+  public final void incrementRelease() {
+    release++;
+  }
+
+  /** increment the build field. */
+  public final void incrementBuild() {
+    build++;
+  }
+
+  /** decrement the major field. */
+  public final void decrementMajor() {
+    major--;
+  }
+
+  /** decrement the minor field. */
+  public final void decrementMinor() {
+    minor--;
+  }
+
+  /** decrement the release field. */
+  public final void decrementRelease() {
+    release--;
+  }
+
+  /** decrement the build field. */
+  public final void decrementBuild() {
+    build--;
+  }
+
+  /**
+   * is this version geater than the destination version?
+   * 
+   * @param dest
+   *          destination version
+   * @return <code>true</code> if this version is greater than the destination
+   *         version, else <code>false</code>
+   */
+  public final boolean greaterThan(final Version dest) {
+    if (equals(dest)) {
+      return false;
+    }
+    if (major != dest.major) {
+      return major > dest.major;
+    }
+    if (minor != dest.minor) {
+      return minor > dest.minor;
+    }
+    if (release != dest.release) {
+      return release > dest.release;
+    }
+    if (build != dest.build) {
+      return build > dest.build;
+    }
+
+    if (hasVersionState()) {
+      return !isSnapshot();
+    } else if (dest.hasVersionState()) {
+      return dest.isSnapshot();
+    }
+    return false;
+  }
+
+  /**
+   * is this version lesser than the destination version?
+   * 
+   * @param dest
+   *          destination version
+   * @return <code>true</code> if this version is lesser than the destination
+   *         version, else <code>false</code>
+   */
+  public final boolean lesserThan(final Version dest) {
+    return !equals(dest) && !greaterThan(dest);
+  }
+
+  /**
+   * is this version equal to the destination version?
+   * 
+   * @param arg0
+   *          destination version
+   * @return <code>true</code> if this version is eqaul than the destination
+   *         version, else <code>false</code>
+   */
+  public final boolean equals(final Object arg0) {
+    if (arg0 instanceof Version) {
+      Version dest = (Version) arg0;
+      if (major != dest.major) {
+        return false;
+      }
+      if (minor != dest.minor) {
+        return false;
+      }
+      if (release != dest.release) {
+        return false;
+      }
+      if (build != dest.build) {
+        return false;
+      }
+      if (hasVersionState()) {
+        if (!versionState.equals(dest.getVersionState())) {
+          return false;
+        }
+      } else {
+        return !dest.hasVersionState();
+      }
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * converting this version to an version string.
+   * 
+   * @return String
+   */
+  public final String toString() {
+    StringBuffer stringBuffer = new StringBuffer(20);
+    stringBuffer.append(major);
+    if (minor >= 0) {
+      stringBuffer.append(".").append(minor);
+      if (release >= 0) {
+        stringBuffer.append(".").append(release);
+        if (build >= 0) {
+          stringBuffer.append(".").append(build);
+        }
+      }
+    }
+    if (hasVersionState()) {
+      stringBuffer.append(VERSION_STATE_MARKER).append(getVersionState());
+    }
+    return stringBuffer.toString();
+  }
+
+  public boolean hasVersionState() {
+    return (versionState != null) && !versionState.equals("");
+  }
+
+  /**
+   * getting the build field.
+   * 
+   * @return int
+   */
+  public final int getBuild() {
+    return build;
+  }
+
+  /**
+   * getting the major field.
+   * 
+   * @return int
+   */
+  public final int getMajor() {
+    return major;
+  }
+
+  /**
+   * getting the minor field.
+   * 
+   * @return int
+   */
+  public final int getMinor() {
+    return minor;
+  }
+
+  /**
+   * getting the release field.
+   * 
+   * @return int
+   */
+  public final int getRelease() {
+    return release;
+  }
+
+  /**
+   * getting the hashcode.
+   * 
+   * @return int
+   */
+  public final int hashCode() {
+    int hash = major ^ minor ^ release ^ build;
+    return hash;
+  }
+
+  /**
+   * @param aBuild
+   *          setting the build number.
+   */
+  public final void setBuild(final int aBuild) {
+    this.build = aBuild;
+  }
+
+  /**
+   * @param aMajor
+   *          setting the major number.
+   */
+  public final void setMajor(final int aMajor) {
+    this.major = aMajor;
+  }
+
+  /**
+   * @param aMinor
+   *          setting the minor number.
+   */
+  public final void setMinor(final int aMinor) {
+    this.minor = aMinor;
+  }
+
+  /**
+   * @param aRelease
+   *          setting the release number.
+   */
+  public final void setRelease(final int aRelease) {
+    this.release = aRelease;
+  }
+
+  public void setVersionState(String versionState) {
+    this.versionState = versionState;
+  }
+
+  public String getVersionState() {
+    return versionState;
+  }
+
+  public boolean isSnapshot() {
+    if (versionState == null) {
+      return false;
+    }
+    return versionState.equals(SNAPSHOT_MARKER);
+  }
+}

+ 76 - 0
src/main/java/de/mcs/utils/WorkingThread.java

@@ -0,0 +1,76 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2006 by MCS
+ * -------------------------------------- Created on 11.04.2006 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+/**
+ * This is the working thread class needed for the threadedpool.
+ * 
+ * @author w.klaas
+ * 
+ */
+public class WorkingThread extends Thread {
+
+    /** my runnabe interface. */
+    private Runnable run = null;
+
+    /** just nothing to do. */
+    private boolean free = true;
+
+    /**
+     * setting a new runnable.
+     * 
+     * @param runit
+     *            the runnable.
+     */
+    public final void setRunnable(final Runnable runit) {
+        this.run = runit;
+    }
+
+    /**
+     * @see java.lang.Thread#run()
+     */
+    @Override
+    public final void run() {
+        setBusy();
+        if (null != run) {
+            run.run();
+        }
+        setFree();
+    }
+
+    /**
+     * @return Returns the free.
+     */
+    public final boolean isFree() {
+        return free;
+    }
+
+    /**
+     * setting the free state.
+     */
+    public final void setFree() {
+        this.free = true;
+    }
+
+    /**
+     * setting this thread to busy.
+     */
+    public final void setBusy() {
+        this.free = false;
+    }
+
+}

+ 591 - 0
src/main/java/de/mcs/utils/XMLChar.java

@@ -0,0 +1,591 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XMLChar.java,v 1.2.4.1 2005/09/15 08:16:01 suresh_emailid Exp $
+ */
+
+package de.mcs.utils;
+
+/**
+ * This class defines the basic XML character properties. The data in this class
+ * can be used to verify that a character is a valid XML character or if the
+ * character is a space, name start, or name character.
+ * <p>
+ * A series of convenience methods are supplied to ease the burden of the
+ * developer. Because inlining the checks can improve per character performance,
+ * the tables of character properties are public. Using the character as an
+ * index into the <code>CHARS</code> array and applying the appropriate mask
+ * flag (e.g. <code>MASK_VALID</code>), yields the same results as calling the
+ * convenience methods. There is one exception: check the comments for the
+ * <code>isValid</code> method for details.
+ *
+ * @author Glenn Marcy, IBM
+ * @author Andy Clark, IBM
+ * @author Eric Ye, IBM
+ * @author Arnaud Le Hors, IBM
+ * @author Rahul Srivastava, Sun Microsystems Inc.
+ *
+ * @version $Id: XMLChar.java,v 1.6 2007/07/19 04:37:19 ofung Exp $
+ */
+public class XMLChar {
+
+  //
+  // Constants
+  //
+
+  /** Character flags. */
+  private static final byte[] CHARS = new byte[1 << 16];
+
+  /** Valid character mask. */
+  public static final int MASK_VALID = 0x01;
+
+  /** Space character mask. */
+  public static final int MASK_SPACE = 0x02;
+
+  /** Name start character mask. */
+  public static final int MASK_NAME_START = 0x04;
+
+  /** Name character mask. */
+  public static final int MASK_NAME = 0x08;
+
+  /** Pubid character mask. */
+  public static final int MASK_PUBID = 0x10;
+
+  /**
+   * Content character mask. Special characters are those that can be considered
+   * the start of markup, such as '&lt;' and '&amp;'. The various newline
+   * characters are considered special as well. All other valid XML characters
+   * can be considered content.
+   * <p>
+   * This is an optimization for the inner loop of character scanning.
+   */
+  public static final int MASK_CONTENT = 0x20;
+
+  /** NCName start character mask. */
+  public static final int MASK_NCNAME_START = 0x40;
+
+  /** NCName character mask. */
+  public static final int MASK_NCNAME = 0x80;
+
+  //
+  // Static initialization
+  //
+
+  static {
+
+    //
+    // [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] |
+    // [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+    //
+
+    int charRange[] = { 0x0009, 0x000A, 0x000D, 0x000D, 0x0020, 0xD7FF, 0xE000, 0xFFFD, };
+
+    //
+    // [3] S ::= (#x20 | #x9 | #xD | #xA)+
+    //
+
+    int spaceChar[] = { 0x0020, 0x0009, 0x000D, 0x000A, };
+
+    //
+    // [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
+    // CombiningChar | Extender
+    //
+
+    int nameChar[] = { 0x002D, 0x002E, // '-' and '.'
+    };
+
+    //
+    // [5] Name ::= (Letter | '_' | ':') (NameChar)*
+    //
+
+    int nameStartChar[] = { 0x003A, 0x005F, // ':' and '_'
+    };
+
+    //
+    // [13] PubidChar ::= #x20 | 0xD | 0xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
+    //
+
+    int pubidChar[] = { 0x000A, 0x000D, 0x0020, 0x0021, 0x0023, 0x0024, 0x0025, 0x003D, 0x005F };
+
+    int pubidRange[] = { 0x0027, 0x003B, 0x003F, 0x005A, 0x0061, 0x007A };
+
+    //
+    // [84] Letter ::= BaseChar | Ideographic
+    //
+
+    int letterRange[] = {
+        // BaseChar
+        0x0041, 0x005A, 0x0061, 0x007A, 0x00C0, 0x00D6, 0x00D8, 0x00F6, 0x00F8, 0x0131, 0x0134, 0x013E, 0x0141, 0x0148,
+        0x014A, 0x017E, 0x0180, 0x01C3, 0x01CD, 0x01F0, 0x01F4, 0x01F5, 0x01FA, 0x0217, 0x0250, 0x02A8, 0x02BB, 0x02C1,
+        0x0388, 0x038A, 0x038E, 0x03A1, 0x03A3, 0x03CE, 0x03D0, 0x03D6, 0x03E2, 0x03F3, 0x0401, 0x040C, 0x040E, 0x044F,
+        0x0451, 0x045C, 0x045E, 0x0481, 0x0490, 0x04C4, 0x04C7, 0x04C8, 0x04CB, 0x04CC, 0x04D0, 0x04EB, 0x04EE, 0x04F5,
+        0x04F8, 0x04F9, 0x0531, 0x0556, 0x0561, 0x0586, 0x05D0, 0x05EA, 0x05F0, 0x05F2, 0x0621, 0x063A, 0x0641, 0x064A,
+        0x0671, 0x06B7, 0x06BA, 0x06BE, 0x06C0, 0x06CE, 0x06D0, 0x06D3, 0x06E5, 0x06E6, 0x0905, 0x0939, 0x0958, 0x0961,
+        0x0985, 0x098C, 0x098F, 0x0990, 0x0993, 0x09A8, 0x09AA, 0x09B0, 0x09B6, 0x09B9, 0x09DC, 0x09DD, 0x09DF, 0x09E1,
+        0x09F0, 0x09F1, 0x0A05, 0x0A0A, 0x0A0F, 0x0A10, 0x0A13, 0x0A28, 0x0A2A, 0x0A30, 0x0A32, 0x0A33, 0x0A35, 0x0A36,
+        0x0A38, 0x0A39, 0x0A59, 0x0A5C, 0x0A72, 0x0A74, 0x0A85, 0x0A8B, 0x0A8F, 0x0A91, 0x0A93, 0x0AA8, 0x0AAA, 0x0AB0,
+        0x0AB2, 0x0AB3, 0x0AB5, 0x0AB9, 0x0B05, 0x0B0C, 0x0B0F, 0x0B10, 0x0B13, 0x0B28, 0x0B2A, 0x0B30, 0x0B32, 0x0B33,
+        0x0B36, 0x0B39, 0x0B5C, 0x0B5D, 0x0B5F, 0x0B61, 0x0B85, 0x0B8A, 0x0B8E, 0x0B90, 0x0B92, 0x0B95, 0x0B99, 0x0B9A,
+        0x0B9E, 0x0B9F, 0x0BA3, 0x0BA4, 0x0BA8, 0x0BAA, 0x0BAE, 0x0BB5, 0x0BB7, 0x0BB9, 0x0C05, 0x0C0C, 0x0C0E, 0x0C10,
+        0x0C12, 0x0C28, 0x0C2A, 0x0C33, 0x0C35, 0x0C39, 0x0C60, 0x0C61, 0x0C85, 0x0C8C, 0x0C8E, 0x0C90, 0x0C92, 0x0CA8,
+        0x0CAA, 0x0CB3, 0x0CB5, 0x0CB9, 0x0CE0, 0x0CE1, 0x0D05, 0x0D0C, 0x0D0E, 0x0D10, 0x0D12, 0x0D28, 0x0D2A, 0x0D39,
+        0x0D60, 0x0D61, 0x0E01, 0x0E2E, 0x0E32, 0x0E33, 0x0E40, 0x0E45, 0x0E81, 0x0E82, 0x0E87, 0x0E88, 0x0E94, 0x0E97,
+        0x0E99, 0x0E9F, 0x0EA1, 0x0EA3, 0x0EAA, 0x0EAB, 0x0EAD, 0x0EAE, 0x0EB2, 0x0EB3, 0x0EC0, 0x0EC4, 0x0F40, 0x0F47,
+        0x0F49, 0x0F69, 0x10A0, 0x10C5, 0x10D0, 0x10F6, 0x1102, 0x1103, 0x1105, 0x1107, 0x110B, 0x110C, 0x110E, 0x1112,
+        0x1154, 0x1155, 0x115F, 0x1161, 0x116D, 0x116E, 0x1172, 0x1173, 0x11AE, 0x11AF, 0x11B7, 0x11B8, 0x11BC, 0x11C2,
+        0x1E00, 0x1E9B, 0x1EA0, 0x1EF9, 0x1F00, 0x1F15, 0x1F18, 0x1F1D, 0x1F20, 0x1F45, 0x1F48, 0x1F4D, 0x1F50, 0x1F57,
+        0x1F5F, 0x1F7D, 0x1F80, 0x1FB4, 0x1FB6, 0x1FBC, 0x1FC2, 0x1FC4, 0x1FC6, 0x1FCC, 0x1FD0, 0x1FD3, 0x1FD6, 0x1FDB,
+        0x1FE0, 0x1FEC, 0x1FF2, 0x1FF4, 0x1FF6, 0x1FFC, 0x212A, 0x212B, 0x2180, 0x2182, 0x3041, 0x3094, 0x30A1, 0x30FA,
+        0x3105, 0x312C, 0xAC00, 0xD7A3,
+        // Ideographic
+        0x3021, 0x3029, 0x4E00, 0x9FA5, };
+    int letterChar[] = {
+        // BaseChar
+        0x0386, 0x038C, 0x03DA, 0x03DC, 0x03DE, 0x03E0, 0x0559, 0x06D5, 0x093D, 0x09B2, 0x0A5E, 0x0A8D, 0x0ABD, 0x0AE0,
+        0x0B3D, 0x0B9C, 0x0CDE, 0x0E30, 0x0E84, 0x0E8A, 0x0E8D, 0x0EA5, 0x0EA7, 0x0EB0, 0x0EBD, 0x1100, 0x1109, 0x113C,
+        0x113E, 0x1140, 0x114C, 0x114E, 0x1150, 0x1159, 0x1163, 0x1165, 0x1167, 0x1169, 0x1175, 0x119E, 0x11A8, 0x11AB,
+        0x11BA, 0x11EB, 0x11F0, 0x11F9, 0x1F59, 0x1F5B, 0x1F5D, 0x1FBE, 0x2126, 0x212E,
+        // Ideographic
+        0x3007, };
+
+    //
+    // [87] CombiningChar ::= ...
+    //
+
+    int combiningCharRange[] = { 0x0300, 0x0345, 0x0360, 0x0361, 0x0483, 0x0486, 0x0591, 0x05A1, 0x05A3, 0x05B9, 0x05BB,
+        0x05BD, 0x05C1, 0x05C2, 0x064B, 0x0652, 0x06D6, 0x06DC, 0x06DD, 0x06DF, 0x06E0, 0x06E4, 0x06E7, 0x06E8, 0x06EA,
+        0x06ED, 0x0901, 0x0903, 0x093E, 0x094C, 0x0951, 0x0954, 0x0962, 0x0963, 0x0981, 0x0983, 0x09C0, 0x09C4, 0x09C7,
+        0x09C8, 0x09CB, 0x09CD, 0x09E2, 0x09E3, 0x0A40, 0x0A42, 0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81,
+        0x0A83, 0x0ABE, 0x0AC5, 0x0AC7, 0x0AC9, 0x0ACB, 0x0ACD, 0x0B01, 0x0B03, 0x0B3E, 0x0B43, 0x0B47, 0x0B48, 0x0B4B,
+        0x0B4D, 0x0B56, 0x0B57, 0x0B82, 0x0B83, 0x0BBE, 0x0BC2, 0x0BC6, 0x0BC8, 0x0BCA, 0x0BCD, 0x0C01, 0x0C03, 0x0C3E,
+        0x0C44, 0x0C46, 0x0C48, 0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0C82, 0x0C83, 0x0CBE, 0x0CC4, 0x0CC6, 0x0CC8, 0x0CCA,
+        0x0CCD, 0x0CD5, 0x0CD6, 0x0D02, 0x0D03, 0x0D3E, 0x0D43, 0x0D46, 0x0D48, 0x0D4A, 0x0D4D, 0x0E34, 0x0E3A, 0x0E47,
+        0x0E4E, 0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19, 0x0F71, 0x0F84, 0x0F86, 0x0F8B, 0x0F90,
+        0x0F95, 0x0F99, 0x0FAD, 0x0FB1, 0x0FB7, 0x20D0, 0x20DC, 0x302A, 0x302F, };
+
+    int combiningCharChar[] = { 0x05BF, 0x05C4, 0x0670, 0x093C, 0x094D, 0x09BC, 0x09BE, 0x09BF, 0x09D7, 0x0A02, 0x0A3C,
+        0x0A3E, 0x0A3F, 0x0ABC, 0x0B3C, 0x0BD7, 0x0D57, 0x0E31, 0x0EB1, 0x0F35, 0x0F37, 0x0F39, 0x0F3E, 0x0F3F, 0x0F97,
+        0x0FB9, 0x20E1, 0x3099, 0x309A, };
+
+    //
+    // [88] Digit ::= ...
+    //
+
+    int digitRange[] = { 0x0030, 0x0039, 0x0660, 0x0669, 0x06F0, 0x06F9, 0x0966, 0x096F, 0x09E6, 0x09EF, 0x0A66, 0x0A6F,
+        0x0AE6, 0x0AEF, 0x0B66, 0x0B6F, 0x0BE7, 0x0BEF, 0x0C66, 0x0C6F, 0x0CE6, 0x0CEF, 0x0D66, 0x0D6F, 0x0E50, 0x0E59,
+        0x0ED0, 0x0ED9, 0x0F20, 0x0F29, };
+
+    //
+    // [89] Extender ::= ...
+    //
+
+    int extenderRange[] = { 0x3031, 0x3035, 0x309D, 0x309E, 0x30FC, 0x30FE, };
+
+    int extenderChar[] = { 0x00B7, 0x02D0, 0x02D1, 0x0387, 0x0640, 0x0E46, 0x0EC6, 0x3005, };
+
+    //
+    // SpecialChar ::= '<', '&', '\n', '\r', ']'
+    //
+
+    int specialChar[] = { '<', '&', '\n', '\r', ']', };
+
+    //
+    // Initialize
+    //
+
+    // set valid characters
+    for (int i = 0; i < charRange.length; i += 2) {
+      for (int j = charRange[i]; j <= charRange[i + 1]; j++) {
+        CHARS[j] |= MASK_VALID | MASK_CONTENT;
+      }
+    }
+
+    // remove special characters
+    for (int i = 0; i < specialChar.length; i++) {
+      CHARS[specialChar[i]] = (byte) (CHARS[specialChar[i]] & ~MASK_CONTENT);
+    }
+
+    // set space characters
+    for (int i = 0; i < spaceChar.length; i++) {
+      CHARS[spaceChar[i]] |= MASK_SPACE;
+    }
+
+    // set name start characters
+    for (int i = 0; i < nameStartChar.length; i++) {
+      CHARS[nameStartChar[i]] |= MASK_NAME_START | MASK_NAME | MASK_NCNAME_START | MASK_NCNAME;
+    }
+    for (int i = 0; i < letterRange.length; i += 2) {
+      for (int j = letterRange[i]; j <= letterRange[i + 1]; j++) {
+        CHARS[j] |= MASK_NAME_START | MASK_NAME | MASK_NCNAME_START | MASK_NCNAME;
+      }
+    }
+    for (int i = 0; i < letterChar.length; i++) {
+      CHARS[letterChar[i]] |= MASK_NAME_START | MASK_NAME | MASK_NCNAME_START | MASK_NCNAME;
+    }
+
+    // set name characters
+    for (int i = 0; i < nameChar.length; i++) {
+      CHARS[nameChar[i]] |= MASK_NAME | MASK_NCNAME;
+    }
+    for (int i = 0; i < digitRange.length; i += 2) {
+      for (int j = digitRange[i]; j <= digitRange[i + 1]; j++) {
+        CHARS[j] |= MASK_NAME | MASK_NCNAME;
+      }
+    }
+    for (int i = 0; i < combiningCharRange.length; i += 2) {
+      for (int j = combiningCharRange[i]; j <= combiningCharRange[i + 1]; j++) {
+        CHARS[j] |= MASK_NAME | MASK_NCNAME;
+      }
+    }
+    for (int i = 0; i < combiningCharChar.length; i++) {
+      CHARS[combiningCharChar[i]] |= MASK_NAME | MASK_NCNAME;
+    }
+    for (int i = 0; i < extenderRange.length; i += 2) {
+      for (int j = extenderRange[i]; j <= extenderRange[i + 1]; j++) {
+        CHARS[j] |= MASK_NAME | MASK_NCNAME;
+      }
+    }
+    for (int i = 0; i < extenderChar.length; i++) {
+      CHARS[extenderChar[i]] |= MASK_NAME | MASK_NCNAME;
+    }
+
+    // remove ':' from allowable MASK_NCNAME_START and MASK_NCNAME chars
+    CHARS[':'] &= ~(MASK_NCNAME_START | MASK_NCNAME);
+
+    // set Pubid characters
+    for (int i = 0; i < pubidChar.length; i++) {
+      CHARS[pubidChar[i]] |= MASK_PUBID;
+    }
+    for (int i = 0; i < pubidRange.length; i += 2) {
+      for (int j = pubidRange[i]; j <= pubidRange[i + 1]; j++) {
+        CHARS[j] |= MASK_PUBID;
+      }
+    }
+
+  } // <clinit>()
+
+  //
+  // Public static methods
+  //
+
+  /*
+   * Returns true if the specified character is a supplemental character.
+   *
+   * @param c The character to check.
+   */
+  public static boolean isSupplemental(int c) {
+    return (c >= 0x10000 && c <= 0x10FFFF);
+  }
+
+  /*
+   * Returns true the supplemental character corresponding to the given
+   * surrogates.
+   *
+   * @param h The high surrogate.
+   * 
+   * @param l The low surrogate.
+   */
+  public static int supplemental(char h, char l) {
+    return (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000;
+  }
+
+  /*
+   * Returns the high surrogate of a supplemental character
+   *
+   * @param c The supplemental character to "split".
+   */
+  public static char highSurrogate(int c) {
+    return (char) (((c - 0x00010000) >> 10) + 0xD800);
+  }
+
+  /*
+   * Returns the low surrogate of a supplemental character
+   *
+   * @param c The supplemental character to "split".
+   */
+  public static char lowSurrogate(int c) {
+    return (char) (((c - 0x00010000) & 0x3FF) + 0xDC00);
+  }
+
+  /*
+   * Returns whether the given character is a high surrogate
+   *
+   * @param c The character to check.
+   */
+  public static boolean isHighSurrogate(int c) {
+    return (0xD800 <= c && c <= 0xDBFF);
+  }
+
+  /*
+   * Returns whether the given character is a low surrogate
+   *
+   * @param c The character to check.
+   */
+  public static boolean isLowSurrogate(int c) {
+    return (0xDC00 <= c && c <= 0xDFFF);
+  }
+
+  /*
+   * Returns true if the specified character is valid. This method also checks
+   * the surrogate character range from 0x10000 to 0x10FFFF. <p> If the program
+   * chooses to apply the mask directly to the <code>CHARS</code> array, then
+   * they are responsible for checking the surrogate character range.
+   *
+   * @param c The character to check.
+   */
+  public static boolean isValid(int c) {
+    return (c < 0x10000 && (CHARS[c] & MASK_VALID) != 0) || (0x10000 <= c && c <= 0x10FFFF);
+  } // isValid(int):boolean
+
+  /*
+   * Returns true if the specified character is invalid.
+   *
+   * @param c The character to check.
+   */
+  public static boolean isInvalid(int c) {
+    return !isValid(c);
+  } // isInvalid(int):boolean
+
+  /*
+   * Returns true if the specified character can be considered content.
+   *
+   * @param c The character to check.
+   */
+  public static boolean isContent(int c) {
+    return (c < 0x10000 && (CHARS[c] & MASK_CONTENT) != 0) || (0x10000 <= c && c <= 0x10FFFF);
+  } // isContent(int):boolean
+  /*
+   * Returns true if the specified character can be considered markup. Markup
+   * characters include '&lt;', '&amp;', and '%'.
+   *
+   * @param c The character to check.
+   */
+
+  public static boolean isMarkup(int c) {
+    return c == '<' || c == '&' || c == '%';
+  } // isMarkup(int):boolean
+
+  /*
+   * Returns true if the specified character is a space character as defined by
+   * production [3] in the XML 1.0 specification.
+   *
+   * @param c The character to check.
+   */
+  public static boolean isSpace(int c) {
+    return c < 0x10000 && (CHARS[c] & MASK_SPACE) != 0;
+  } // isSpace(int):boolean
+
+  /*
+   * Returns true if the specified character is a valid name start character as
+   * defined by production [5] in the XML 1.0 specification.
+   *
+   * @param c The character to check.
+   */
+  public static boolean isNameStart(int c) {
+    return c < 0x10000 && (CHARS[c] & MASK_NAME_START) != 0;
+  } // isNameStart(int):boolean
+
+  /*
+   * Returns true if the specified character is a valid name character as
+   * defined by production [4] in the XML 1.0 specification.
+   *
+   * @param c The character to check.
+   */
+  public static boolean isName(int c) {
+    return c < 0x10000 && (CHARS[c] & MASK_NAME) != 0;
+  } // isName(int):boolean
+
+  /*
+   * Returns true if the specified character is a valid NCName start character
+   * as defined by production [4] in Namespaces in XML recommendation.
+   *
+   * @param c The character to check.
+   */
+  public static boolean isNCNameStart(int c) {
+    return c < 0x10000 && (CHARS[c] & MASK_NCNAME_START) != 0;
+  } // isNCNameStart(int):boolean
+
+  /*
+   * Returns true if the specified character is a valid NCName character as
+   * defined by production [5] in Namespaces in XML recommendation.
+   *
+   * @param c The character to check.
+   */
+  public static boolean isNCName(int c) {
+    return c < 0x10000 && (CHARS[c] & MASK_NCNAME) != 0;
+  } // isNCName(int):boolean
+
+  /*
+   * Returns true if the specified character is a valid Pubid character as
+   * defined by production [13] in the XML 1.0 specification.
+   *
+   * @param c The character to check.
+   */
+  public static boolean isPubid(int c) {
+    return c < 0x10000 && (CHARS[c] & MASK_PUBID) != 0;
+  } // isPubid(int):boolean
+
+  /*
+   * [5] Name ::= (Letter | '_' | ':') (NameChar)*
+   */
+  /*
+   * Check to see if a string is a valid Name according to [5] in the XML 1.0
+   * Recommendation
+   *
+   * @param name string to check
+   * 
+   * @return true if name is a valid Name
+   */
+  public static boolean isValidName(String name) {
+    if (name.length() == 0)
+      return false;
+    char ch = name.charAt(0);
+    if (isNameStart(ch) == false)
+      return false;
+    for (int i = 1; i < name.length(); i++) {
+      ch = name.charAt(i);
+      if (isName(ch) == false) {
+        return false;
+      }
+    }
+    return true;
+  } // isValidName(String):boolean
+
+  /*
+   * from the namespace rec [4] NCName ::= (Letter | '_') (NCNameChar)*
+   */
+  /*
+   * Check to see if a string is a valid NCName according to [4] from the XML
+   * Namespaces 1.0 Recommendation
+   *
+   * @param ncName string to check
+   * 
+   * @return true if name is a valid NCName
+   */
+  public static boolean isValidNCName(String ncName) {
+    if (ncName.length() == 0)
+      return false;
+    char ch = ncName.charAt(0);
+    if (isNCNameStart(ch) == false)
+      return false;
+    for (int i = 1; i < ncName.length(); i++) {
+      ch = ncName.charAt(i);
+      if (isNCName(ch) == false) {
+        return false;
+      }
+    }
+    return true;
+  } // isValidNCName(String):boolean
+
+  /*
+   * [7] Nmtoken ::= (NameChar)+
+   */
+  /*
+   * Check to see if a string is a valid Nmtoken according to [7] in the XML 1.0
+   * Recommendation
+   *
+   * @param nmtoken string to check
+   * 
+   * @return true if nmtoken is a valid Nmtoken
+   */
+  public static boolean isValidNmtoken(String nmtoken) {
+    if (nmtoken.length() == 0)
+      return false;
+    for (int i = 0; i < nmtoken.length(); i++) {
+      char ch = nmtoken.charAt(i);
+      if (!isName(ch)) {
+        return false;
+      }
+    }
+    return true;
+  } // isValidName(String):boolean
+
+  // encodings
+
+  /*
+   * Returns true if the encoding name is a valid IANA encoding. This method
+   * does not verify that there is a decoder available for this encoding, only
+   * that the characters are valid for an IANA encoding name.
+   *
+   * @param ianaEncoding The IANA encoding name.
+   */
+  public static boolean isValidIANAEncoding(String ianaEncoding) {
+    if (ianaEncoding != null) {
+      int length = ianaEncoding.length();
+      if (length > 0) {
+        char c = ianaEncoding.charAt(0);
+        if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
+          for (int i = 1; i < length; i++) {
+            c = ianaEncoding.charAt(i);
+            if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '.' && c != '_'
+                && c != '-') {
+              return false;
+            }
+          }
+          return true;
+        }
+      }
+    }
+    return false;
+  } // isValidIANAEncoding(String):boolean
+
+  /*
+   * Returns true if the encoding name is a valid Java encoding. This method
+   * does not verify that there is a decoder available for this encoding, only
+   * that the characters are valid for an Java encoding name.
+   *
+   * @param javaEncoding The Java encoding name.
+   */
+  public static boolean isValidJavaEncoding(String javaEncoding) {
+    if (javaEncoding != null) {
+      int length = javaEncoding.length();
+      if (length > 0) {
+        for (int i = 1; i < length; i++) {
+          char c = javaEncoding.charAt(i);
+          if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '.' && c != '_'
+              && c != '-') {
+            return false;
+          }
+        }
+        return true;
+      }
+    }
+    return false;
+  } // isValidIANAEncoding(String):boolean
+
+  /*
+   * Simple check to determine if qname is legal. If it returns false then
+   * <param>str</param> is illegal; if it returns true then <param>str</param>
+   * is legal.
+   */
+  public static boolean isValidQName(String str) {
+
+    final int colon = str.indexOf(':');
+
+    if (colon == 0 || colon == str.length() - 1) {
+      return false;
+    }
+
+    if (colon > 0) {
+      final String prefix = str.substring(0, colon);
+      final String localPart = str.substring(colon + 1);
+      return isValidNCName(prefix) && isValidNCName(localPart);
+    } else {
+      return isValidNCName(str);
+    }
+  }
+
+} // class XMLChar

+ 174 - 0
src/main/java/de/mcs/utils/XMLConverter.java

@@ -0,0 +1,174 @@
+/*
+ * EASY Enterprise Engine
+ * Copyright (c) 2004 by EASY Software AG
+ * --------------------------------------
+ * Created on 30.01.2004 by W.Klaas
+ */
+package de.mcs.utils;
+
+import java.io.UnsupportedEncodingException;
+
+import de.mcs.utils.codecs.Base64;
+
+/**
+ * Dieses Klasse dient der Zeichenkonvertierung f�r XML.
+ * 
+ * @author W.Klaas
+ */
+public final class XMLConverter {
+  /** supress instancing. */
+  private XMLConverter() {
+  }
+
+  private static final String BOUNDARY_BASE64 = "{b64}";
+
+  public static void main(String[] args) {
+    System.out.println("1:" + convertDataToXML("test von Zeichen."));
+    System.out.println("2:" + convertDataToXML("test & < > ' \" Zeichen."));
+    System.out.println("3:" + convertDataToXML("&test von Zeichen.>"));
+    System.out.println("4:" + convertDataToXML("&"));
+  }
+
+  /**
+   * Konvertiert die speziellen XML Sonderzeichen in die Entities
+   * 
+   * @param data
+   *          zu konvertierenden Eingangsstring
+   * @return konvertierter String
+   */
+  public static String convertDataToXML(final String data) {
+    char[] chars = data.toCharArray();
+    StringBuffer result = new StringBuffer();
+    if (data != null) {
+      int length = data.length();
+      int fpos = 0;
+      for (int i = 0; i < length; i++) {
+        char ch = chars[i];
+        switch (ch) {
+        case '&':
+          result.append(data.substring(fpos, i));
+          result.append("&amp;");
+          fpos = i + 1;
+          break;
+        case '<':
+          result.append(data.substring(fpos, i));
+          result.append("&lt;");
+          fpos = i + 1;
+          break;
+        case '>':
+          result.append(data.substring(fpos, i));
+          result.append("&gt;");
+          fpos = i + 1;
+          break;
+        case '"':
+          result.append(data.substring(fpos, i));
+          result.append("&quot;");
+          fpos = i + 1;
+          break;
+        case '\'':
+          result.append(data.substring(fpos, i));
+          result.append("&apos;");
+          fpos = i + 1;
+          break;
+        default:
+          break;
+        }
+      }
+      if (fpos < length) {
+        result.append(data.substring(fpos, length));
+      }
+    }
+    return result.toString();
+  }
+
+  /**
+   * Diese Routine erzeugt einen CDATA Abschnitt.
+   * 
+   * @param data
+   *          Eingangsstring der in einen CDATA Teil konvertiert werden soll
+   * @return konvertierter Ausgangsstring
+   */
+  public static String convertDataToCDATA(final String data) {
+    String mydata = "<![CDATA[" + data + "]]>";
+    return mydata;
+  }
+
+  /**
+   * THis function determine if the string data has illegal XML characters like
+   * #00.
+   * 
+   * @param data
+   *          String to determine
+   * @return boolean true, if there are illegal charaters, false if everything
+   *         is ok for XML
+   */
+  public static boolean mustEncode(final String data) {
+    if (data != null) {
+      return hasIllegalXMLChar(data) | (data.indexOf("]]>") >= 0);
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * THis function determine if the string data has illegal XML characters like
+   * #00.
+   * 
+   * @param data
+   *          String to determine
+   * @return boolean true, if there are illegal charaters, false if everything
+   *         is ok for XML
+   */
+  public static boolean hasIllegalXMLChar(final String data) {
+    if (data != null) {
+      char[] chars = data.toCharArray();
+      boolean result = false;
+      for (int n = 0; n < chars.length && !result; n++) {
+        result = result | XMLChar.isInvalid(chars[n]);
+      }
+      return result;
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * automatically encode string data into an xml conform string. If non valid
+   * xml charaters are present, the string will be base64 coded.
+   * 
+   * @param data
+   *          the data to encode
+   * @return encoded string
+   */
+  public static String encodeStringData(final String data) {
+    String value = data;
+    if (XMLConverter.mustEncode(value)) {
+      try {
+        value = BOUNDARY_BASE64 + Base64.encodeBytes(value.getBytes("UTF-8"));
+      } catch (UnsupportedEncodingException e) {
+        throw new Error(e);
+      }
+    }
+    return value;
+  }
+
+  /**
+   * automatically decode string data from an xml conform string. If string is
+   * base64 coded it will be automatically converted back to string.
+   * 
+   * @param data
+   *          the data to decode
+   * @return decoded string
+   */
+  public static String decodeStringData(final String data) {
+    String value = data;
+    if (value.startsWith(BOUNDARY_BASE64)) {
+      try {
+        value = new String(Base64.decode(value.substring(BOUNDARY_BASE64.length())), "UTF-8");
+      } catch (UnsupportedEncodingException e) {
+        throw new Error(e);
+      }
+    }
+    return value;
+  }
+}

+ 249 - 0
src/main/java/de/mcs/utils/ZipExtracter.java

@@ -0,0 +1,249 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AccessControlException;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Extract files from a .ear, .war or .jar archive file.
+ * 
+ * @author j.froehlich
+ */
+public class ZipExtracter {
+
+  /** the archive file to work with. */
+  private File archiveFile;
+
+  /** the archive file to work with. */
+  private ZipFile archive;
+
+  /** the base directory. */
+  private String baseDirPath; // OS dependent path
+
+  /** a recursion counter. */
+  private static int recursioncnt = 0;
+
+  /**
+   * @return getting the archive file.
+   */
+  public final File getFile() {
+    return archiveFile;
+  }
+
+  /**
+   * @return the name of the archive
+   * @see java.util.zip.ZipFile#getName()
+   */
+  public final String getName() {
+    return null == archive ? null : archive.getName();
+  }
+
+  /**
+   * converting a filename to a zip name.
+   * 
+   * @param osFilename
+   *          filename to convert
+   * @return converted name.
+   */
+  private String toZipName(final String osFilename) {
+    String zipName = osFilename;
+    if (null != baseDirPath && baseDirPath.length() > 0 && baseDirPath.length() < osFilename.length()
+        && osFilename.startsWith(baseDirPath)) {
+      zipName = osFilename.substring(baseDirPath.length() + 1);
+    }
+    return zipName.replace(File.separatorChar, '/');
+  }
+
+  /**
+   * constructor with the archive name and a working target.
+   * 
+   * @param aArchiveFile
+   *          the archive name
+   * @param aBaseDirFile
+   *          where to extract.
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public ZipExtracter(final File aArchiveFile, final File aBaseDirFile) throws IOException {
+    super();
+    if (!aArchiveFile.exists()) {
+      throw new FileNotFoundException(aArchiveFile.getAbsolutePath());
+    }
+    if (!aArchiveFile.canRead()) {
+      throw new AccessControlException("No read access", new FilePermission(aArchiveFile.getAbsolutePath(), "read"));
+    }
+    this.archiveFile = aArchiveFile;
+
+    if (null == aBaseDirFile || 0 == aBaseDirFile.getName().length()) {
+      baseDirPath = new File(System.getProperty("java.io.tmpdir")).getPath();
+    } else {
+      baseDirPath = aBaseDirFile.getPath();
+    }
+
+    toZipName(aArchiveFile.getPath());
+  }
+
+  /**
+   * constructor with the archive name and a working target.
+   * 
+   * @param archiveFilename
+   *          the archive name
+   * @param baseDir
+   *          where to extract.
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public ZipExtracter(final String archiveFilename, final String baseDir) throws IOException {
+    this(new File(archiveFilename), new File(baseDir));
+  }
+
+  /**
+   * doing the extraction.
+   * 
+   * @param fromFilename
+   *          filename in zip file
+   * @param toLocalFilename
+   *          local file name
+   * @return File
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public final File extract(final String fromFilename, final String toLocalFilename) throws IOException {
+    if (null == archive) {
+      archive = new ZipFile(archiveFile);
+    }
+
+    File localFilename;
+    if (null == toLocalFilename) {
+      localFilename = new File(this.baseDirPath, fromFilename);
+    } else {
+      localFilename = new File(toLocalFilename);
+    }
+    File parent = localFilename.getParentFile();
+    if (null != parent) {
+      parent.mkdirs();
+    }
+
+    Enumeration<? extends ZipEntry> archiveEntries = archive.entries();
+    byte[] buffer = new byte[8 * 1024];
+    while (archiveEntries.hasMoreElements()) {
+      ZipEntry entry = (ZipEntry) archiveEntries.nextElement();
+      if (!entry.isDirectory() && entry.getName().equalsIgnoreCase(fromFilename) && entry.getSize() > 0) {
+        // --- extracting ---
+        InputStream input = archive.getInputStream(entry);
+        FileOutputStream output = new FileOutputStream(localFilename);
+        int len;
+        while ((len = input.read(buffer, 0, buffer.length)) != -1) {
+          output.write(buffer, 0, len);
+        }
+        output.close();
+        // ------------------
+        return localFilename;
+      }
+    }
+    throw new FileNotFoundException(
+        "The file '" + fromFilename + "' is not contained in '" + archive.getName() + " or is a directory.");
+
+  }
+
+  /**
+   * closing the zip file.
+   * 
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public final void close() throws IOException {
+    if (null != archive) {
+      archive.close();
+      archive = null;
+    }
+  }
+
+  /**
+   * @see java.lang.Object#toString()
+   */
+  public final String toString() {
+    return archiveFile.toString();
+  }
+
+  /**
+   * extract all files from the zip file.
+   * 
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public final void extractAll() throws IOException {
+    extractAll(false, 0);
+  }
+
+  /**
+   * extract all files from the zip file, with all or none subfolder, or with
+   * subfolder up to the defined depth..
+   * 
+   * @param recursive
+   *          should all subfolder be extracted.
+   * @param recursionDepth
+   *          if recursive = true, how depth-
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public final void extractAll(final boolean recursive, final int recursionDepth) throws IOException {
+    if (null == archive) {
+      archive = new ZipFile(archiveFile);
+    }
+
+    Enumeration<? extends ZipEntry> archiveEntries = archive.entries();
+    byte[] buffer = new byte[8 * 1024];
+    while (archiveEntries.hasMoreElements()) {
+      ZipEntry entry = (ZipEntry) archiveEntries.nextElement();
+      if (!entry.isDirectory() && entry.getSize() > 0) {
+        // --- extracting ---
+        File outfile = new File(baseDirPath, entry.getName());
+        outfile.getParentFile().mkdirs();
+        InputStream input = archive.getInputStream(entry);
+        FileOutputStream output = new FileOutputStream(outfile);
+        int len;
+        while ((len = input.read(buffer, 0, buffer.length)) != -1) {
+          output.write(buffer, 0, len);
+        }
+        output.close();
+        input.close();
+        // --- recursion ---
+        // in case of a .zip .war .ear or .jar file, we can unzip them
+        // too! just if u want to...
+        if (recursive && entry.getName().matches("(.*[(e)|(w)|(j)]ar)|(.*zip)") && recursioncnt < recursionDepth) {
+          recursioncnt++;
+          File subdir = new File(baseDirPath, entry.getName().substring(0, entry.getName().length() - 4));
+          File subjar = new File(baseDirPath, entry.getName());
+          ZipExtracter xarex = new ZipExtracter(subjar, subdir);
+          xarex.extractAll(true, recursionDepth);
+          xarex.close();
+          recursioncnt--;
+        }
+      }
+    }
+  }
+}

+ 290 - 0
src/main/java/de/mcs/utils/ZipUpdater.java

@@ -0,0 +1,290 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.security.AccessControlException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.jar.JarOutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Modifies files in an .ear or .jar archive file.
+ */
+public class ZipUpdater {
+
+  /** the archive file to work with. */
+  private File archiveFile;
+
+  /** the archive file to work with. */
+  private ZipInputStream archive;
+
+  /** the base directory. */
+  private String baseDirPath; // OS dependent path
+
+  /** temp name of a file. */
+  private String zipname;
+
+  /**
+   * converting a filename to a zip name.
+   * 
+   * @param osFilename
+   *          filename to convert
+   * @return converted name.
+   */
+  private String toZipName(final String osFilename) {
+    String zipName = osFilename;
+    if (null != baseDirPath && baseDirPath.length() > 0 && baseDirPath.length() < osFilename.length()
+        && osFilename.startsWith(baseDirPath)) {
+
+      zipName = osFilename.substring(baseDirPath.length() + 1);
+    }
+    return zipName.replace(File.separatorChar, '/');
+  }
+
+  /**
+   * Constructing the xar updater.
+   * 
+   * @param aArchiveFile
+   *          The archive file to use.
+   * @param baseDirFile
+   *          the base file to work with.
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public ZipUpdater(final File aArchiveFile, final File baseDirFile) throws IOException {
+    super();
+    if (!aArchiveFile.exists()) {
+      throw new FileNotFoundException(aArchiveFile.getAbsolutePath());
+    }
+    if (!aArchiveFile.canWrite()) {
+      throw new AccessControlException("No write access", new FilePermission(aArchiveFile.getAbsolutePath(), "write"));
+    }
+    this.archiveFile = aArchiveFile;
+
+    // File(".");
+    if (null == baseDirFile) {
+      throw new NullPointerException("baseDirFile should not be null.");
+    }
+    if (!baseDirFile.exists()) {
+      throw new FileNotFoundException(baseDirFile.getCanonicalPath());
+    }
+    if (0 == baseDirFile.getName().length()) {
+      baseDirPath = new File(System.getProperty("java.io.tmpdir")).getPath(); // new
+    } else {
+      baseDirPath = baseDirFile.getPath();
+    }
+
+    zipname = toZipName(aArchiveFile.getPath());
+  }
+
+  /**
+   * Constructing the xar updater.
+   * 
+   * @param archiveFilename
+   *          The archive file to use.
+   * @param baseDir
+   *          the base file to work with.
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public ZipUpdater(final String archiveFilename, final String baseDir) throws IOException {
+    this(new File(archiveFilename), new File(baseDir));
+  }
+
+  /**
+   * @return getting the archive file.
+   */
+  public final File getFile() {
+    return archiveFile;
+  }
+
+  /** some special dir and file names. */
+  static final String METAINF_NAME = "META-INF/";
+
+  /** some special dir and file names. */
+  static final String INDEX_NAME = METAINF_NAME + "INDEX.LIST";
+
+  /** some special dir and file names. */
+  static final String MANIFEST_NAME = METAINF_NAME + "MANIFEST.MF";
+
+  /**
+   * adding a file to the zip.
+   * 
+   * @param zipoutputstream
+   *          the outputstream
+   * @param aName
+   *          name of the entry
+   * @param file
+   *          the file to add
+   * @param buffer
+   *          a buffer
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public final void addFile(final ZipOutputStream zipoutputstream, final String aName, final File file,
+      final byte[] buffer) throws IOException {
+    String name = aName;
+    boolean isDir = file.isDirectory();
+    if (isDir) {
+      name = name.endsWith("/") ? name : name + "/";
+    }
+    if (name.startsWith("/")) {
+      name = name.substring(1);
+    } else if (name.startsWith("./")) {
+      name = name.substring(2);
+    }
+
+    if (name.equals("") || name.equals(".") || name.equals(zipname)) {
+      return;
+    }
+
+    long l = isDir ? 0L : file.length();
+
+    // System.out.print("is file:" + name);
+    // System.out.println(" length:" + Long.toString(l));
+
+    ZipEntry zipentry = new ZipEntry(name);
+    zipentry.setTime(file.lastModified());
+    zipentry.setMethod(ZipEntry.DEFLATED);
+    if (l == 0L) {
+      zipentry.setMethod(0);
+      zipentry.setSize(0L);
+      zipentry.setCrc(0L);
+    }
+    zipoutputstream.putNextEntry(zipentry);
+    if (!isDir) {
+      BufferedInputStream bufferedinputstream = new BufferedInputStream(new FileInputStream(file));
+      int i;
+      while ((i = bufferedinputstream.read(buffer, 0, buffer.length)) > 0) {
+        zipoutputstream.write(buffer, 0, i);
+      }
+
+      bufferedinputstream.close();
+    }
+    zipoutputstream.closeEntry();
+
+  }
+
+  /**
+   * update an archive.
+   * 
+   * @param toArchiveFile
+   *          the archive file to update
+   * @param files
+   *          the files to zip
+   * @param asNames
+   *          as which names
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public final void update(final File toArchiveFile, final File[] files, final String[] asNames) throws IOException {
+
+    if (null == archive) {
+      archive = new ZipInputStream(new FileInputStream(archiveFile));
+    }
+
+    if (null == files) {
+      return;
+    }
+    Hashtable<String, File> hashtable = new Hashtable<String, File>();
+    for (int index = 0; index < files.length; index++) {
+      String name = toZipName(files[index].getPath());
+      if (null != asNames && null != asNames[index]) {
+        name = asNames[index];
+      }
+      hashtable.put(name, files[index]);
+    }
+    hashtable.remove(INDEX_NAME); // currently no META-INF/INDEX.LIST
+    // support at all
+    hashtable.remove(MANIFEST_NAME); // currently no META-INF/MANIFEST.MF
+    // update support
+
+    JarOutputStream jaroutputstream = new JarOutputStream(new FileOutputStream(toArchiveFile));
+    jaroutputstream.setLevel(java.util.zip.Deflater.BEST_COMPRESSION);
+
+    byte[] buffer = new byte[8 * 1024];
+    ZipEntry zipentry;
+    while (null != (zipentry = archive.getNextEntry())) {
+      String name = zipentry.getName();
+      if (!name.equalsIgnoreCase(INDEX_NAME)) {
+        if (!hashtable.containsKey(name)) {
+          ZipEntry newEntry = new ZipEntry(name);
+          newEntry.setMethod(zipentry.getMethod());
+          newEntry.setTime(zipentry.getTime());
+          newEntry.setComment(zipentry.getComment());
+          newEntry.setExtra(zipentry.getExtra());
+          if (ZipEntry.STORED == zipentry.getMethod()) {
+            newEntry.setSize(zipentry.getSize());
+            newEntry.setCrc(zipentry.getCrc());
+          }
+          jaroutputstream.putNextEntry(newEntry);
+          int len;
+          while ((len = archive.read(buffer, 0, buffer.length)) != -1) {
+            jaroutputstream.write(buffer, 0, len);
+          }
+        } else {
+          addFile(jaroutputstream, name, (File) hashtable.get(name), buffer);
+          hashtable.remove(name);
+        }
+      }
+    }
+
+    if (!hashtable.isEmpty()) {
+      for (Iterator<Entry<String, File>> iter = hashtable.entrySet().iterator(); iter.hasNext();) {
+        Entry<String, File> entry = iter.next();
+        addFile(jaroutputstream, entry.getKey(), entry.getValue(), buffer);
+      }
+    }
+
+    jaroutputstream.close();
+
+  }
+
+  /**
+   * closing the zip file.
+   * 
+   * @throws IOException
+   *           if something goes wrong.
+   */
+
+  public final void close() throws IOException {
+    if (null != archive) {
+      archive.close();
+      archive = null;
+    }
+  }
+
+  /**
+   * @return string representing this object.
+   * @see java.lang.Object#toString()
+   */
+  public final String toString() {
+    return archiveFile.toString();
+  }
+
+}

+ 582 - 0
src/main/java/de/mcs/utils/caches/BlobCache.java

@@ -0,0 +1,582 @@
+package de.mcs.utils.caches;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+import de.mcs.utils.Files;
+import de.mcs.utils.StringUtils;
+
+/**
+ * Diese Klasse dient der Verwaltung von zwischengespeicherten Blobs auf dem EEX
+ * XMLServer. This class is the blob server part. In this class all blobs will
+ * be registered and administrated.
+ * 
+ * @author w.klaas
+ */
+public class BlobCache {
+
+  class KeyAlreadyExistsException extends Exception {
+
+    /**
+         * 
+         */
+    private static final long serialVersionUID = 715224362894259926L;
+
+    public KeyAlreadyExistsException(String string) {
+      super(string);
+    }
+
+  }
+
+  /** internal map for the blobs. */
+  private HashMap<String, BlobCacheItem> hmBlobs = null;
+
+  /** internal map for the blobs. */
+  private HashMap<String, String> hmExternal = null;
+
+  /** max files in a folder. */
+  private final int MAXFILECOUNT = 2000;
+
+  private ArrayList<CacheFolder> folders;
+
+  private File blobPath;
+
+  private CacheFolder actualFolder;
+
+  private CacheFolder folder;
+
+  private boolean subfolders;
+
+  /**
+   * Diese Klasse dient der Aufnahme eines einzelnen Blobs.
+   * 
+   * @author w.klaas
+   */
+  class BlobCacheItem implements Serializable {
+    /**
+         * 
+         */
+    private static final long serialVersionUID = -871711242126446479L;
+
+    /** filename of this entry. */
+    private String sFilename;
+
+    /** ID of this entry. */
+    private String sID;
+
+    /** last access to this item. */
+    private Date dAccess;
+
+    private String fileMD5;
+
+    private String externalKey;
+
+    /**
+     * Eine neue Datei registrieren, inkl. MD5 Hash bestimmen
+     * 
+     * @param filename
+     *          Filename of the file
+     * @param refid
+     *          this is the registered id of the document
+     * @param persistent
+     *          Should this blob be persistent?
+     */
+    public BlobCacheItem(final String filename, final String externalKey) {
+      sFilename = filename;
+      fileMD5 = Files.computeMD5FromFile(new File(sFilename));
+      // wenn wir keine refid haben ist der MD5 der Datei nicht sehr
+      // eindeutig
+      // selbst mit refid ist es nicht eindeutig genug
+      // darum machen wir noch einen MD5 der MD5 + Timestamp.
+      sID = StringUtils.md5String(fileMD5 + (new Date()).getTime());
+      dAccess = new Date();
+      this.externalKey = externalKey;
+    }
+
+    /**
+     * Die Blobdatei wieder freigeben.
+     */
+    public void freeResources() {
+      new File(sFilename).delete();
+    }
+
+    /**
+     * Vor dem abräumen, wird die Datei gelöscht.
+     */
+    protected void finalize() {
+      freeResources();
+    }
+
+    public String getFilename() {
+      return sFilename;
+    }
+
+    public void recalcBlobID() {
+      sID = StringUtils.md5String(fileMD5 + (new Date()).getTime());
+    }
+
+    public String getExternalKey() {
+      return externalKey;
+    }
+
+    public String toString() {
+      StringBuffer buffer = new StringBuffer();
+      buffer.append("filename:");
+      buffer.append(sFilename);
+      buffer.append(",ID:");
+      buffer.append(sID);
+      return buffer.toString();
+    }
+  }
+
+  /**
+   * This class represent a cache folder in the file system.
+   * 
+   * @author w.klaas
+   */
+  class CacheFolder {
+    private int count;
+
+    private File path;
+
+    private int folderNumber;
+
+    private int maxCount;
+
+    /**
+     * constructor for a cache folder. Creates automatically the folder.
+     * 
+     * @param blobPath
+     *          path where the folder lives.
+     * @param folderCount
+     *          number of the folder.
+     * @throws IOException
+     */
+    public CacheFolder(final File blobPath, final int folderCount, final int aMaxCount) throws IOException {
+      folderNumber = folderCount;
+      maxCount = aMaxCount;
+      path = new File(blobPath, Integer.toHexString(folderNumber));
+      path.mkdirs();
+
+      // now deleting all files in this folder
+      File[] files = path.listFiles();
+      for (int i = 0; i < files.length; i++) {
+        File file = files[i];
+        file.delete();
+      }
+      new FolderBlocker(path, true);
+      initFolder();
+    }
+
+    /**
+     * initialise the folder. (at this time only recount)
+     */
+    private void initFolder() {
+      recount();
+    }
+
+    /**
+     * counting all data files.
+     */
+    private void recount() {
+      if (path != null) {
+        synchronized (path) {
+          File[] files = path.listFiles(new FilenameFilter() {
+            public boolean accept(File dir, String name) {
+              return name.endsWith(".bin");
+            }
+          });
+          count = files.length;
+        }
+      }
+    }
+
+    /**
+     * Anfordern eines neuen Blob Dateinamens.
+     * 
+     * @return String Name der Datei
+     * @throws IOException
+     *           Wenn was schiefgeht.
+     */
+    public final String getNewBlobName(final String prefix, final String suffix) throws IOException {
+      String fileName = "";
+      synchronized (path) {
+        if (count > maxCount) {
+          throw new IOException("to many files in this folder");
+        }
+        fileName = Files.getTempFileName(prefix, suffix, path.getAbsolutePath());
+        count++;
+      }
+      return fileName;
+    }
+
+    /**
+     * @return the count
+     */
+    public final int getCount() {
+      return count;
+    }
+
+    /**
+     * @return the folderNumber
+     */
+    public final int getFolderNumber() {
+      return folderNumber;
+    }
+
+    /**
+     * @return the path
+     */
+    public final File getPath() {
+      return path;
+    }
+
+    /**
+     * the folder can create a new file.
+     * 
+     * @return boolean.
+     */
+    public boolean canCreate() {
+      return count < maxCount;
+    }
+
+  }
+
+  /**
+   * Constructs a new blob cache.
+   * 
+   * @param blobCachePath
+   *          where to store the blob files.
+   * @param useSubFolders
+   *          using subfolders for more than 2000 files.
+   * @throws IOException
+   *           if soemthing goes wrong
+   */
+  public BlobCache(final String blobCachePath, final boolean useSubFolders) throws IOException {
+    blobPath = new File(blobCachePath);
+    hmBlobs = new HashMap<String, BlobCacheItem>(1000, 100);
+    hmExternal = new HashMap<String, String>(100, 100);
+    this.subfolders = useSubFolders;
+    initBlobCache();
+  }
+
+  public BlobCache(File cachePath, boolean useSubFolders) throws IOException {
+    blobPath = cachePath;
+    hmBlobs = new HashMap<String, BlobCacheItem>(1000, 100);
+    hmExternal = new HashMap<String, String>(100, 100);
+    this.subfolders = useSubFolders;
+    initBlobCache();
+  }
+
+  private void initBlobCache() throws IOException {
+    if (!blobPath.exists()) {
+      blobPath.mkdirs();
+    }
+    new FolderBlocker(blobPath, true);
+
+    if (subfolders) {
+      folders = new ArrayList<CacheFolder>(10);
+      addCacheFolder();
+    }
+  }
+
+  /**
+   * @param blobPath
+   * @param folders
+   * @param folderCount
+   * @throws IOException
+   */
+  private CacheFolder addCacheFolder() throws IOException {
+    CacheFolder folder = new CacheFolder(blobPath, folders.size(), MAXFILECOUNT);
+    folders.add(folder);
+    return folder;
+  }
+
+  /**
+   * Anfordern eines neuen Blob Dateinamens.
+   * 
+   * @param prefix
+   *          the prefix for the blobname
+   * @param suffix
+   *          the suffix for the blobname
+   * @return String Name der Datei
+   * @throws IOException
+   *           Wenn was schiefgeht.
+   */
+  public final String getNewBlobName(final String prefix, final String suffix) throws IOException {
+    String fileName = "";
+    if (!subfolders) {
+      fileName = Files.getTempFileName(prefix, suffix, blobPath.getCanonicalPath());
+    } else {
+      synchronized (folders) {
+        if ((actualFolder == null) || !actualFolder.canCreate()) {
+          actualFolder = null;
+          for (Iterator<CacheFolder> iter = folders.iterator(); iter.hasNext();) {
+            folder = iter.next();
+            if (folder.canCreate()) {
+              actualFolder = folder;
+            }
+          }
+          if (actualFolder == null) {
+            actualFolder = addCacheFolder();
+          }
+        }
+        fileName = actualFolder.getNewBlobName(prefix, suffix);
+      }
+    }
+    if ((fileName == null) || fileName.equals("")) {
+      throw new IOException("can't create a new filename.");
+    }
+    return fileName;
+  }
+
+  /**
+   * Registering the file in the blobcache. The file is only registered if the
+   * id is not already in the cache.
+   * 
+   * @param blobName
+   *          filename of the file to register
+   * @return String the id to find the file in the cache.
+   */
+  public final String registerBlob(final String blobName) {
+    BlobCacheItem myBlob = new BlobCacheItem(blobName, null);
+    if (!hasBlob(myBlob.sID)) {
+      synchronized (hmBlobs) {
+        hmBlobs.put(myBlob.sID, myBlob);
+      }
+    }
+    return myBlob.sID;
+  }
+
+  /**
+   * Registering the file in the blobcache. The file is only registered if the
+   * id is not already in the cache.
+   * 
+   * @param blobName
+   *          filename of the file to register
+   * @param externalKey
+   *          the external key to set for this blob
+   * @return String the id to find the file in the cache.
+   * @throws KeyAlreadyExistsException
+   *           if the key already exists
+   */
+  public final String registerBlob(final String blobName, final String externalKey) throws KeyAlreadyExistsException {
+    if (hasExternalKey(externalKey)) {
+      throw new KeyAlreadyExistsException("the extrnal key is already in use.");
+    }
+    BlobCacheItem myBlob = new BlobCacheItem(blobName, externalKey);
+    if (!hasBlob(myBlob.sID)) {
+      synchronized (hmBlobs) {
+        hmBlobs.put(myBlob.sID, myBlob);
+        if (externalKey != null) {
+          hmExternal.put(externalKey, myBlob.sID);
+        }
+      }
+    }
+    return myBlob.sID;
+  }
+
+  /**
+   * Pr�fen, ob ein bestimmtes Blob auch zur Verf�gung steht.
+   * 
+   * @param blobID
+   *          die ID des Blobs
+   * @return boolean
+   */
+  public final boolean hasBlob(final String blobID) {
+    boolean hasId;
+    synchronized (hmBlobs) {
+      hasId = hmBlobs.containsKey(blobID);
+    }
+    return hasId;
+  }
+
+  /**
+   * Pr�fen, ob ein bestimmtes Blob mit extranl KEy auch zur Verf�gung steht.
+   * 
+   * @param externalKey
+   *          die ID des Blobs
+   * @return boolean
+   */
+  public final boolean hasExternalKey(final String externalKey) {
+    boolean hasId;
+    synchronized (hmExternal) {
+      hasId = hmExternal.containsKey(externalKey);
+    }
+    return hasId;
+  }
+
+  /**
+   * Blobid eines bestimmtes Blob mit extranl KEy holen.
+   * 
+   * @param externalKey
+   *          die ID des Blobs
+   * @return string, blobid des blobs, oder <code>null</code> wenn nicht
+   *         vorhanden
+   */
+  public final String getIDfromExternalKey(final String externalKey) {
+    String blobId = null;
+    synchronized (hmExternal) {
+      if (hmExternal.containsKey(externalKey)) {
+        blobId = hmExternal.get(externalKey);
+      }
+    }
+    return blobId;
+  }
+
+  /**
+   * Den Dateinamen eines Blobs holen, gleichzeitig wird das Blob für die
+   * nächste zeitspanne freigeschaltet. with security.
+   * 
+   * @param blobID
+   *          die ID des Blobs
+   * @return String Dateiname des Blobs
+   * @throws IOException
+   *           if soemthing goes wrong
+   */
+  public final String getBlobFileName(final String blobID) throws IOException {
+    String sBlobFileName = null;
+    synchronized (hmBlobs) {
+      if (hasBlob(blobID)) {
+        BlobCacheItem myBlob = (BlobCacheItem) hmBlobs.get(blobID);
+        myBlob.dAccess = new Date();
+        sBlobFileName = myBlob.sFilename;
+      }
+      return sBlobFileName;
+    }
+  }
+
+  /**
+   * Den Dateinamen eines Blobs holen, gleichzeitig wird das Blob für die
+   * nächste zeitspanne freigeschaltet. with security.
+   * 
+   * @param blobID
+   *          die ID des Blobs
+   * @return String Dateiname des Blobs
+   * @throws IOException
+   *           if soemthing goes wrong
+   */
+  public final String getBlobProperties(final String blobID) throws IOException {
+    Properties props = new Properties();
+    String sBlobFileName = "";
+    synchronized (hmBlobs) {
+      if (hasBlob(blobID)) {
+        BlobCacheItem myBlob = (BlobCacheItem) hmBlobs.get(blobID);
+        myBlob.dAccess = new Date();
+        sBlobFileName = myBlob.sFilename;
+        props.setProperty("filename", sBlobFileName);
+        props.setProperty("length", Long.toString(new File(myBlob.sFilename).length()));
+        props.setProperty("md5", myBlob.fileMD5);
+        props.setProperty("id", myBlob.sID);
+        sBlobFileName = props.toString();
+      }
+    }
+    return sBlobFileName;
+  }
+
+  /**
+   * Hier werden jetzt alle Blobs die älter sind als die Angabe, deregistriert.
+   * Bei der nächsten GC werden diese dann gelöscht
+   * 
+   * @param sec
+   *          Anzahl der Sekunden die die Blobs alt sein dürfen
+   */
+  public final void freeBlobs(final long sec) {
+    Date myEqualDate = new Date(new Date().getTime() - (sec * 1000));
+    BlobCacheItem myBlobItem;
+    ArrayList<String> vDelKeys = new ArrayList<String>(hmBlobs.size());
+    String sKey = "";
+    List<String> filenames = new ArrayList<String>();
+    synchronized (hmBlobs) {
+      Iterator<String> it = hmBlobs.keySet().iterator();
+      while (it.hasNext()) {
+        sKey = (String) it.next();
+        myBlobItem = (BlobCacheItem) hmBlobs.get(sKey);
+        if (myBlobItem.getFilename() != null) {
+          File file = new File(myBlobItem.getFilename());
+          filenames.add(file.getName());
+          if (!file.exists()) {
+            vDelKeys.add(sKey);
+          }
+        }
+        if (myEqualDate.after(myBlobItem.dAccess)) {
+          // now deleting the file
+          myBlobItem.freeResources();
+          vDelKeys.add(sKey);
+        }
+      }
+      it = null;
+      for (int n = 0; n < vDelKeys.size(); n++) {
+        hmBlobs.remove((String) vDelKeys.get(n));
+      }
+      vDelKeys = null;
+    }
+    synchronized (hmExternal) {
+      Iterator<Entry<String, String>> it = hmExternal.entrySet().iterator();
+      while (it.hasNext()) {
+        Entry<String, String> entry = it.next();
+        if (!hasBlob(entry.getValue())) {
+          it.remove();
+        }
+      }
+    }
+    if (folders != null) {
+      // jetzt alle cache folder aktualisieren
+      for (Iterator<CacheFolder> iter = folders.iterator(); iter.hasNext();) {
+        CacheFolder folder = iter.next();
+        folder.recount();
+      }
+    }
+  }
+
+  /**
+   * Holt die aktuelle Anzahl der im Cache liegenden Blobs.
+   * 
+   * @return int Anzahl der Blobs im Cache
+   */
+  public final int getBlobCacheCount() {
+    int size;
+    synchronized (hmBlobs) {
+      size = hmBlobs.size();
+    }
+    return size;
+  }
+
+  /**
+   * Vor dem abräumen, wird die Datei gelöscht.
+   */
+  public final void freeResources() {
+    // alle Blobs löschen
+    freeBlobs(0);
+  }
+
+  /**
+   * Besort eine Liste aller BlobID's.
+   * 
+   * @return String[]
+   */
+  public final String[] getBlobIDs() {
+    String[] keys;
+    synchronized (hmBlobs) {
+      keys = new String[hmBlobs.size()];
+      Iterator<String> it = hmBlobs.keySet().iterator();
+      int n = 0;
+      while (it.hasNext()) {
+        keys[n] = (String) it.next();
+        n++;
+      }
+      it = null;
+    }
+    return keys;
+  }
+
+}

+ 78 - 0
src/main/java/de/mcs/utils/caches/FolderBlocker.java

@@ -0,0 +1,78 @@
+/**
+ * 
+ */
+package de.mcs.utils.caches;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.util.Properties;
+
+/**
+ * @author w.klaas
+ */
+public class FolderBlocker {
+
+  private File messFile;
+
+  private RandomAccessFile fosdirlock;
+
+  private FileLock lock;
+
+  public FolderBlocker(final File path, final boolean block) throws IOException {
+    messFile = new File(path, "Don't_mess_with_this_files.txt");
+    if (!messFile.exists()) {
+      Properties messProps = new Properties();
+      try {
+        messProps.store(new FileOutputStream(messFile), "Don't mess with this files");
+      } catch (Exception e) {
+      }
+    }
+    if (block) {
+      // now blocking the folder
+      try {
+        File dirLock = new File(path, ".dirlock");
+        fosdirlock = new RandomAccessFile(dirLock, "rws");
+        FileChannel f = fosdirlock.getChannel();
+        lock = f.tryLock();
+        if (lock != null) {
+          dirLock.deleteOnExit();
+          ByteBuffer bytes = ByteBuffer.allocate(8); // a long is 8 bytes
+          bytes.putLong(System.currentTimeMillis() + 10000).flip();
+          f.write(bytes); // Write the buffer contents to the channel
+          f.force(false); // Force them out to the disk
+        } else {
+          throw new OverlappingFileLockException();
+        }
+      } catch (FileNotFoundException e) {
+        // e.printStackTrace();
+      }
+    }
+  }
+
+  public void freeResources() {
+    if (fosdirlock != null) {
+      try {
+        // Closing the RandomAccessFile also closes its FileChannel.
+        if (lock != null && lock.isValid()) {
+          lock.release();
+        }
+        fosdirlock.close();
+        fosdirlock = null;
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  protected void finalize() throws Throwable {
+    super.finalize();
+    freeResources();
+  }
+}

+ 21 - 0
src/main/java/de/mcs/utils/caches/KeyAlreadyExistsException.java

@@ -0,0 +1,21 @@
+/**
+ * 
+ */
+package de.mcs.utils.caches;
+
+/**
+ * @author w.klaas
+ * 
+ */
+public class KeyAlreadyExistsException extends Exception {
+
+  /**
+         * 
+         */
+  private static final long serialVersionUID = 715224362894259926L;
+
+  public KeyAlreadyExistsException(String string) {
+    super(string);
+  }
+
+}

+ 618 - 0
src/main/java/de/mcs/utils/caches/ObjectCache.java

@@ -0,0 +1,618 @@
+package de.mcs.utils.caches;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.UUID;
+
+/**
+ * Diese Klasse dient dem automatischen Cachen von beliebigen Objekten.
+ * 
+ * @author w.klaas
+ */
+public class ObjectCache<E> {
+
+  public interface ObjectListener<E> {
+    void onDelete(E item, String externalKey);
+  }
+
+  // private List<CacheItem> objectList = null;
+
+  public enum TIMEMODE {
+    ACCESS, CREATION
+  }
+
+  /** internal map for the objects. */
+  private Map<String, CacheItem<E>> objectMap = null;
+
+  /** internal map for the blobs. */
+  private Map<String, String> objectMapExternal = null;
+
+  private int maxCount;
+
+  private Timer timer = null;
+
+  private ObjectListener<E> objectListener = null;
+
+  private TIMEMODE timemode = TIMEMODE.ACCESS;
+
+  private Comparator<? super CacheItem<E>> comperator = new Comparator<CacheItem<E>>() {
+
+    @Override
+    public int compare(CacheItem<E> o1, CacheItem<E> o2) {
+      try {
+        int cmp = 0;
+        if ((o1 == null) && (o2 == null)) {
+          cmp = 0;
+        } else if (o1 == null) {
+          cmp = -1;
+        } else if (o2 == null) {
+          cmp = 1;
+        } else {
+          Long d1 = o1.getAccess();
+          Long d2 = o2.getAccess();
+          cmp = d1.compareTo(d2);
+        }
+        return cmp;
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+      return 0;
+    }
+
+  };
+
+  /**
+   * Diese Klasse dient der Aufnahme eines einzelnen Blobs.
+   * 
+   * @author w.klaas
+   */
+  class CacheItem<F extends E> implements Serializable {
+    /**
+           * 
+           */
+    private static final long serialVersionUID = -871711242126446479L;
+
+    /** ID of this entry. */
+    private String sID = UUID.randomUUID().toString();
+
+    /** last access to this item. */
+    private Date dAccess = new Date();
+
+    private String externalKey;
+
+    private F object;
+
+    public CacheItem() {
+      setAccess();
+    }
+
+    public long getAccess() {
+      synchronized (this) {
+        if (dAccess != null) {
+          return dAccess.getTime();
+        }
+        return 0;
+      }
+    }
+
+    /**
+     * Eine neue Datei registrieren, inkl. MD5 Hash bestimmen
+     * 
+     * @param filename
+     *          Filename of the file
+     * @param refid
+     *          this is the registered id of the document
+     * @param persistent
+     *          Should this blob be persistent?
+     */
+    public CacheItem(final F object, final String externalKey) {
+      this();
+      this.object = object;
+      this.externalKey = externalKey;
+    }
+
+    /**
+     * Aufräumen
+     */
+    public void freeResources() {
+      // nothing to do here;
+    }
+
+    /**
+     * Vor dem abräumen, aufräumen.
+     */
+    protected void finalize() {
+      freeResources();
+    }
+
+    public F getObject() {
+      return object;
+    }
+
+    public String getExternalKey() {
+      return externalKey;
+    }
+
+    public String toString() {
+      StringBuffer buffer = new StringBuffer();
+      buffer.append("object:");
+      buffer.append(object.toString());
+      buffer.append(",ID:");
+      buffer.append(sID);
+      return buffer.toString();
+    }
+
+    public void setAccess() {
+      synchronized (this) {
+        dAccess = new Date();
+      }
+    }
+  }
+
+  public ObjectCache() {
+    this(-1);
+  }
+
+  /**
+   * Constructs a new object cache.
+   * 
+   * @param maxCount
+   *          maximal count of objects in this cache.
+   */
+  public ObjectCache(final int maxCount) {
+    this.maxCount = maxCount;
+    int size = maxCount > 0 ? maxCount : 1000;
+    if (maxCount >= 0) {
+      objectMap = new HashMap<String, CacheItem<E>>(size, 100);
+      objectMapExternal = new HashMap<String, String>(size, 100);
+      // objectList = new ArrayList<CacheItem>(size);
+    } else {
+      objectMap = new HashMap<String, CacheItem<E>>();
+      objectMapExternal = new HashMap<String, String>();
+    }
+    initCache();
+  }
+
+  private void initCache() {
+    // nothing to do here
+  }
+
+  /**
+   * Registering an object in the cache.
+   * 
+   * @param object
+   *          object to add
+   * @return String the id of the object in the cache.
+   */
+  public final String addObject(final E object) {
+    try {
+      return addObject(null, object);
+    } catch (KeyAlreadyExistsException e) {
+      // can never occure
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /**
+   * Registering an object in the cache.
+   * 
+   * @param externalKey
+   *          the external key to use with this object
+   * @param object
+   *          object to add
+   * @return String the id of the object in the cache.
+   * @throws KeyAlreadyExistsException
+   *           the key already exists
+   */
+  public final String addObject(final String externalKey, final E object) throws KeyAlreadyExistsException {
+    if ((externalKey != null) && (hasExternalKey(externalKey))) {
+      throw new KeyAlreadyExistsException("the external key is already in use.");
+    }
+    CacheItem<E> cacheItem = new CacheItem<E>(object, externalKey);
+    if (!hasObject(cacheItem.sID)) {
+      checkSize();
+      synchronized (objectMap) {
+        objectMap.put(cacheItem.sID, cacheItem);
+        if (externalKey != null) {
+          objectMapExternal.put(externalKey, cacheItem.sID);
+        }
+      }
+    }
+    return cacheItem.sID;
+  }
+
+  private void checkSize() {
+    if (maxCount > 0) {
+      while (objectMap.size() >= (maxCount)) {
+        removeOldest();
+      }
+    }
+  }
+
+  private void removeOldest() {
+    List<CacheItem<E>> list = null;
+    synchronized (objectMap) {
+      list = new ArrayList<CacheItem<E>>(objectMap.values());
+      synchronized (comperator) {
+        Collections.sort(list, comperator);
+      }
+      CacheItem<E> item = list.get(0);
+      removeObject(item.sID);
+    }
+  }
+
+  /**
+   * Prüfen, ob ein bestimmtes Object auch zur Verfügung steht.
+   * 
+   * @param objectID
+   *          die ID des Objektes
+   * @return boolean
+   */
+  public final boolean hasObject(final String objectID) {
+    boolean hasId = false;
+    hasId = objectMap.containsKey(objectID);
+    return hasId;
+  }
+
+  /**
+   * Prüfen, ob ein bestimmtes Objekt mit exteranl Key auch zur Verfügung steht.
+   * 
+   * @param externalKey
+   *          die ID des Blobs
+   * @return boolean
+   */
+  public final boolean hasExternalKey(final String externalKey) {
+    boolean hasId = false;
+    synchronized (objectMapExternal) {
+      hasId = objectMapExternal.containsKey(externalKey);
+    }
+    return hasId;
+  }
+
+  /**
+   * Objektid eines bestimmtes Objektes mit external Key holen.
+   * 
+   * @param externalKey
+   *          die ID des Objektes
+   * @return string, id des Objektes, oder <code>null</code> wenn nicht
+   *         vorhanden
+   */
+  public final String getIDfromExternalKey(final String externalKey) {
+    String objectId = null;
+    synchronized (objectMapExternal) {
+      if (objectMapExternal.containsKey(externalKey)) {
+        objectId = objectMapExternal.get(externalKey);
+      }
+    }
+    return objectId;
+  }
+
+  public final String getExternalKeyfromID(final String objectId) {
+    synchronized (objectMapExternal) {
+      if (objectMapExternal.containsValue(objectId)) {
+        Optional<Entry<String, String>> findFirst = objectMapExternal.entrySet().stream()
+            .filter(entry -> entry.getValue().equals(objectId)).findFirst();
+        if (findFirst.isPresent()) {
+          return findFirst.get().getKey();
+        }
+      }
+    }
+    return null;
+  }
+
+  private E getObjectInternal(String objectID) {
+    E object = null;
+    synchronized (objectMap) {
+      if (hasObject(objectID)) {
+        CacheItem<E> cacheItem = objectMap.get(objectID);
+        object = cacheItem.object;
+      }
+    }
+    return object;
+  }
+
+  /**
+   * Das Objekt holen, gleichzeitig wird das Objekt für die nächste Zeitspanne
+   * freigeschaltet.
+   * 
+   * @param objectID
+   *          die ID des Objektes
+   * @return the object or null
+   */
+  public final E getObject(final String objectID) {
+    E object = null;
+    synchronized (objectMap) {
+      if (hasObject(objectID)) {
+        CacheItem<E> cacheItem = objectMap.get(objectID);
+        if (TIMEMODE.ACCESS.equals(timemode)) {
+          cacheItem.setAccess();
+        }
+        object = cacheItem.object;
+      }
+    }
+    return object;
+  }
+
+  /**
+   * Das älteste Objekt holen, gleichzeitig wird das Objekt für die nächste
+   * Zeitspanne freigeschaltet.
+   * 
+   * @return the object or null
+   */
+  public final E getOldestObject() {
+    E object = null;
+    synchronized (objectMap) {
+      List<CacheItem<E>> list = new ArrayList<CacheItem<E>>(objectMap.values());
+      synchronized (comperator) {
+        Collections.sort(list, comperator);
+      }
+      if (list.size() == 0) {
+        return null;
+      }
+
+      CacheItem<E> cacheItem = list.get(0);
+      if (TIMEMODE.ACCESS.equals(timemode)) {
+        cacheItem.setAccess();
+      }
+      object = cacheItem.object;
+
+      return (E) object;
+    }
+  }
+
+  /**
+   * Das Objekt holen, gleichzeitig wird das Objekt für die nächste Zeitspanne
+   * freigeschaltet.
+   * 
+   * @param externalKey
+   *          die ID des Objektes
+   * @return the object or null
+   */
+  public final E getObjectFromExternalKey(final String externalKey) {
+    E object = null;
+    String objectID = getIDfromExternalKey(externalKey);
+    if (objectID != null) {
+      return getObject(objectID);
+    }
+    return object;
+  }
+
+  public void touchExternalKey(String externalKey) {
+    String objectID = getIDfromExternalKey(externalKey);
+    if (objectID != null) {
+      getObject(objectID);
+    }
+  }
+
+  public void touch(String objectID) {
+    getObject(objectID);
+  }
+
+  /**
+   * Das Objekt löschen.
+   * 
+   * @param objectID
+   *          die ID des Objektes
+   * @return the removed object
+   */
+  public final E removeObject(final String objectID) {
+    E object = null;
+    synchronized (objectMap) {
+      if (hasObject(objectID)) {
+        String externalKey = getExternalKeyfromID(objectID);
+        CacheItem<E> cacheItem = objectMap.remove(objectID);
+        if (cacheItem != null) {
+          if (objectMapExternal.containsKey(cacheItem.getExternalKey())) {
+            objectMapExternal.remove(cacheItem.getExternalKey());
+          }
+          object = cacheItem.object;
+          if (objectListener != null) {
+            objectListener.onDelete(cacheItem.getObject(), externalKey);
+          }
+        }
+      }
+    }
+    return object;
+  }
+
+  /**
+   * Das Objekt löschen.
+   * 
+   * @param externalKey
+   *          die ID des Objektes
+   * @return the removed object
+   */
+  public final E removeObjectFromExternalKey(final String externalKey) {
+    E object = null;
+    String objectID = getIDfromExternalKey(externalKey);
+    if (objectID != null) {
+      return removeObject(objectID);
+    }
+    return object;
+  }
+
+  /**
+   * Hier werden jetzt alle Objekte die älter sind als die Angabe,
+   * deregistriert. Bei der nächsten GC werden diese dann gelöscht
+   * 
+   * @param sec
+   *          Anzahl der Sekunden die die Blobs alt sein d�rfen
+   */
+  public final void freeObjects(final long sec) {
+    Date myEqualDate = new Date(new Date().getTime() - (sec * 1000));
+    CacheItem<E> cacheItem;
+    ArrayList<String> vDelKeys = new ArrayList<String>(objectMap.size());
+    String sKey = "";
+    synchronized (objectMap) {
+      Iterator<String> it = objectMap.keySet().iterator();
+      while (it.hasNext()) {
+        sKey = it.next();
+        cacheItem = objectMap.get(sKey);
+        if (myEqualDate.getTime() > (cacheItem.getAccess())) {
+          cacheItem.freeResources();
+          vDelKeys.add(sKey);
+        }
+      }
+      it = null;
+      for (int n = 0; n < vDelKeys.size(); n++) {
+        CacheItem<E> item = objectMap.remove(vDelKeys.get(n));
+        if (objectListener != null) {
+          objectListener.onDelete(item.getObject(), item.getExternalKey());
+        }
+      }
+      vDelKeys = null;
+    }
+    synchronized (objectMapExternal) {
+      Iterator<Entry<String, String>> it = objectMapExternal.entrySet().iterator();
+      while (it.hasNext()) {
+        Entry<String, String> entry = it.next();
+        if (!hasObject(entry.getValue())) {
+          it.remove();
+        }
+      }
+    }
+  }
+
+  /**
+   * Holt die aktuelle Anzahl der im Cache liegenden Objekte.
+   * 
+   * @return int Anzahl der Objekte im Cache
+   */
+  public final int getCacheCount() {
+    int size;
+    synchronized (objectMap) {
+      size = objectMap.size();
+    }
+    return size;
+  }
+
+  /**
+   * Cache leeren
+   */
+  public final void freeResources() {
+    freeObjects(0);
+  }
+
+  /**
+   * Besort eine Liste aller Objekt ID's.
+   * 
+   * @return String[]
+   */
+  public final String[] getObjectIDs() {
+    String[] keys;
+    synchronized (objectMap) {
+      keys = new String[objectMap.size()];
+      Iterator<String> it = objectMap.keySet().iterator();
+      int n = 0;
+      while (it.hasNext()) {
+        keys[n] = (String) it.next();
+        n++;
+      }
+      it = null;
+    }
+    return keys;
+  }
+
+  /**
+   * starting the cleanuptask for this object cache
+   * 
+   * @param cleanupTime
+   *          time to cleanup in sec.
+   * @param taskName
+   *          name of the thread.
+   */
+  public void startCleanupTask(final int cleanupTime, final String taskName) {
+    if (timer != null) {
+      stopCleanupTask();
+    }
+    if (timer == null) {
+      timer = new Timer(taskName, true);
+      timer.scheduleAtFixedRate(new TimerTask() {
+
+        @Override
+        public void run() {
+          freeObjects(cleanupTime);
+        }
+      }, cleanupTime * 1000, cleanupTime * 1000);
+    }
+
+  }
+
+  /**
+   * stopping the cleanup task.
+   */
+  public void stopCleanupTask() {
+    if (timer != null) {
+      timer.cancel();
+      timer = null;
+    }
+  }
+
+  public void registerObjectListener(ObjectListener<E> listener) {
+    this.objectListener = listener;
+  }
+
+  public void removeObjectListener() {
+    this.objectListener = null;
+  }
+
+  public Collection<E> values() {
+    Collection<E> vs = new ArrayList<E>(objectMap.size());
+    synchronized (objectMap) {
+      for (CacheItem<E> cacheItem : objectMap.values()) {
+        vs.add(cacheItem.getObject());
+      }
+    }
+    return vs;
+  }
+
+  public static void main(String[] args) throws InterruptedException {
+    ObjectCache<Integer> cache = new ObjectCache<Integer>(10);
+    for (int i = 0; i < 20; i++) {
+      System.out.print(i + ":" + cache.getCacheCount() + ":");
+      for (String string : cache.getObjectIDs()) {
+        System.out.print(cache.getObjectInternal(string));
+        System.out.print(",");
+      }
+      System.out.println();
+      try {
+        cache.addObject(Integer.toString(i), new Integer(i));
+      } catch (KeyAlreadyExistsException e) {
+        e.printStackTrace();
+      }
+      Thread.sleep(1000);
+    }
+  }
+
+  /**
+   * @return the timemode
+   */
+  public TIMEMODE getTimemode() {
+    return timemode;
+  }
+
+  /**
+   * @param timemode
+   *          the timemode to set
+   */
+  public void setTimemode(TIMEMODE timemode) {
+    this.timemode = timemode;
+  }
+
+  public long size() {
+    return objectMap.size();
+  }
+}

+ 103 - 0
src/main/java/de/mcs/utils/caches/TimedCache.java

@@ -0,0 +1,103 @@
+/**
+ * 
+ */
+package de.mcs.utils.caches;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * @author wklaa_000
+ *
+ */
+public class TimedCache<T extends TimedCacheEntry> {
+
+  private final Map<String, T> localCache = new HashMap<>();
+  private final Object ThreadLock = new Object();
+  private Thread thread;
+  private int count;
+
+  /**
+   * 
+   */
+  public TimedCache() {
+    count = 0;
+  }
+
+  public void put(T entry) {
+    synchronized (localCache) {
+      localCache.put(entry.getKey(), entry);
+    }
+  }
+
+  public boolean hasEntry(String key) {
+    return localCache.containsKey(key);
+  }
+
+  public T get(String key) {
+    return localCache.get(key);
+  }
+
+  public void removeOldEntries(long cacheInvalidationTime) {
+    List<String> toDelete = new ArrayList<>();
+    synchronized (localCache) {
+      for (Entry<String, T> entry : localCache.entrySet()) {
+        if ((entry.getValue().getTime() + cacheInvalidationTime) < System.currentTimeMillis()) {
+          toDelete.add(entry.getKey());
+        }
+      }
+      for (String key : toDelete) {
+        localCache.remove(key);
+      }
+    }
+  }
+
+  public void asyncRemoveEntries(final long cacheInvalidationTime) {
+    if ((thread != null) && thread.isAlive()) {
+      return;
+    }
+    synchronized (ThreadLock) {
+      if ((thread != null) && thread.isAlive()) {
+        return;
+      }
+      count++;
+      thread = new Thread(new Runnable() {
+
+        @Override
+        public void run() {
+          try {
+            removeOldEntries(cacheInvalidationTime);
+          } catch (Exception e) {
+            e.printStackTrace();
+          }
+        }
+      });
+      thread.start();
+    }
+  }
+
+  /**
+   * 
+   */
+  public void clear() {
+    localCache.clear();
+  }
+
+  /**
+   * @return
+   */
+  public int size() {
+    return localCache.size();
+  }
+
+  /**
+   * @return the count
+   */
+  protected int getCount() {
+    return count;
+  }
+
+}

+ 49 - 0
src/main/java/de/mcs/utils/caches/TimedCacheEntry.java

@@ -0,0 +1,49 @@
+/**
+ * 
+ */
+package de.mcs.utils.caches;
+
+/**
+ * @author wklaa_000
+ *
+ */
+public abstract class TimedCacheEntry {
+
+  private String key;
+  private long time;
+
+  /**
+   * 
+   */
+  private TimedCacheEntry() {
+    time = System.currentTimeMillis();
+  }
+
+  public TimedCacheEntry(String key) {
+    this();
+    this.key = key;
+  }
+
+  /**
+   * @return the time
+   */
+  public long getTime() {
+    return time;
+  }
+
+  /**
+   * @param time
+   *          the time to set
+   */
+  public void setTime(long time) {
+    this.time = time;
+  }
+
+  /**
+   * @return the key
+   */
+  public String getKey() {
+    return key;
+  }
+
+}

+ 1377 - 0
src/main/java/de/mcs/utils/codecs/Base64.java

@@ -0,0 +1,1377 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils.codecs;
+
+/**
+ * Encodes and decodes to and from Base64 notation.
+ * <p>
+ * Change Log:
+ * </p>
+ * <ul>
+ * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods.
+ * Added some convenience methods for reading and writing to and from
+ * files.</li>
+ * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on
+ * systems with other encodings (like EBCDIC).</li>
+ * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
+ * encoded data was a single byte.</li>
+ * <li>v2.0 - I got rid of methods that used booleans to set options. Now
+ * everything is more consolidated and cleaner. The code now detects when data
+ * that's being decoded is gzip-compressed and will decompress it automatically.
+ * Generally things are cleaner. You'll probably have to change some method
+ * calls that you were making to support the new options format (<tt>int</tt>s
+ * that you "OR" together).</li>
+ * <li>v1.5.1 - Fixed bug when decompressing and decoding to a byte[] using
+ * <tt>decode( String s, boolean gzipCompressed )</tt>. Added the ability to
+ * "suspend" encoding in the Output Stream so you can turn on and off the
+ * encoding if you need to embed base64 data in an otherwise "normal" stream
+ * (like an XML file).</li>
+ * <li>v1.5 - Output stream pases on flush() command but doesn't do anything
+ * itself. This helps when using GZIP streams. Added the ability to
+ * GZip-compress objects before encoding them.</li>
+ * <li>v1.4 - Added helper methods to read/write files.</li>
+ * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
+ * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input
+ * stream where last buffer being read, if not completely full, was not
+ * returned.</li>
+ * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the
+ * wrong time.</li>
+ * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
+ * </ul>
+ * <p>
+ * I am placing this code in the Public Domain. Do with it as you will. This
+ * software comes with no guarantees or warranties but with plenty of
+ * well-wishing instead! Please visit
+ * <a href="http://iharder.net/base64">http://iharder.net/base64</a>
+ * periodically to check for updates or to contribute improvements.
+ * </p>
+ * 
+ * @author Robert Harder
+ * @author rob@iharder.net
+ * @author w.klaas made this class checkstyle conform.
+ * @version 2.1
+ * @deprecated
+ */
+public final class Base64 {
+
+  /* ******** P U B L I C F I E L D S ******** */
+
+  /** No options specified. Value is zero. */
+  public static final int NO_OPTIONS = 0;
+
+  /** Specify encoding. */
+  public static final int ENCODE = 1;
+
+  /** Specify decoding. */
+  public static final int DECODE = 0;
+
+  /** Specify that data should be gzip-compressed. */
+  public static final int GZIP = 2;
+
+  /** Don't break lines when encoding (violates strict Base64 specification). */
+  public static final int DONT_BREAK_LINES = 8;
+
+  /* ******** P R I V A T E F I E L D S ******** */
+
+  /** Maximum line length (76) of Base64 output. */
+  private static final int MAX_LINE_LENGTH = 76;
+
+  /** The equals sign (=) as a byte. */
+  private static final byte EQUALS_SIGN = (byte) '=';
+
+  /** The new line character (\n) as a byte. */
+  private static final byte NEW_LINE = (byte) '\n';
+
+  /** Preferred encoding. */
+  private static final String PREFERRED_ENCODING = "UTF-8";
+
+  /** The 64 valid Base64 values. */
+  private static final byte[] ALPHABET;
+
+  /** The converting alphabet. */
+  private static final byte[] NATIVE_ALPHABET = /*
+                                                 * May be something funny like
+                                                 * EBCDIC
+                                                 */
+      { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I',
+          (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R',
+          (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
+          (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
+          (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's',
+          (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1',
+          (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+',
+          (byte) '/' };
+
+  /** Determine which ALPHABET to use. */
+  static {
+    byte[] bytes;
+    try {
+      bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes(PREFERRED_ENCODING);
+    } catch (java.io.UnsupportedEncodingException use) {
+      bytes = NATIVE_ALPHABET; // Fall back to native encoding
+    } // end catch
+    ALPHABET = bytes;
+  } // end static
+
+  /**
+   * Translates a Base64 value to either its 6-bit reconstruction value or a
+   * negative number indicating some other meaning.
+   */
+  private static final byte[] DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal
+                                                                                // 0
+                                                                                // -
+                                                                                // 8
+      -5, -5, // Whitespace: Tab and Linefeed
+      -9, -9, // Decimal 11 - 12
+      -5, // Whitespace: Carriage Return
+      -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
+      // 26
+      -9, -9, -9, -9, -9, // Decimal 27 - 31
+      -5, // Whitespace: Space
+      -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
+      62, // Plus sign at decimal 43
+      -9, -9, -9, // Decimal 44 - 46
+      63, // Slash at decimal 47
+      52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
+      -9, -9, -9, // Decimal 58 - 60
+      -1, // Equals sign at decimal 61
+      -9, -9, -9, // Decimal 62 - 64
+      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A'
+      // through 'N'
+      14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
+      // through 'Z'
+      -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
+      26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
+      // through 'm'
+      39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
+      // through 'z'
+      -9, -9, -9, -9 // Decimal 123 - 126
+      /*
+       * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
+       * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
+       * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
+       * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
+       * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
+       * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
+       * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
+       * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
+       * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
+       * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255
+       */
+  };
+
+  // I think I end up not using the BAD_ENCODING indicator.
+  // private final static byte BAD_ENCODING = -9; // Indicates error in
+  // encoding
+  /** Indicates white space in encoding. */
+  private static final byte WHITE_SPACE_ENC = -5;
+
+  /** Indicates equals sign in encoding. */
+  private static final byte EQUALS_SIGN_ENC = -1;
+
+  /** Defeats instantiation. */
+  private Base64() {
+  }
+
+  /* ******** E N C O D I N G M E T H O D S ******** */
+
+  /**
+   * Encodes up to the first three bytes of array <var>threeBytes</var> and
+   * returns a four-byte array in Base64 notation. The actual number of
+   * significant bytes in your array is given by <var>numSigBytes</var>. The
+   * array <var>threeBytes</var> needs only be as big as <var>numSigBytes</var>.
+   * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
+   * 
+   * @param b4
+   *          A reusable byte array to reduce array instantiation
+   * @param threeBytes
+   *          the array to convert
+   * @param numSigBytes
+   *          the number of significant bytes in your array
+   * @return four byte array in Base64 notation.
+   * @since 1.5.1
+   */
+  private static byte[] encode3to4(final byte[] b4, final byte[] threeBytes, final int numSigBytes) {
+    encode3to4(threeBytes, 0, numSigBytes, b4, 0);
+    return b4;
+  } // end encode3to4
+
+  /**
+   * Encodes up to three bytes of the array <var>source</var> and writes the
+   * resulting four Base64 bytes to <var>destination</var>. The source and
+   * destination arrays can be manipulated anywhere along their length by
+   * specifying <var>srcOffset</var> and <var>destOffset</var>. This method does
+   * not check to make sure your arrays are large enough to accomodate
+   * <var>srcOffset</var> + 3 for the <var>source</var> array or
+   * <var>destOffset</var> + 4 for the <var>destination</var> array. The actual
+   * number of significant bytes in your array is given by
+   * <var>numSigBytes</var>.
+   * 
+   * @param source
+   *          the array to convert
+   * @param srcOffset
+   *          the index where conversion begins
+   * @param numSigBytes
+   *          the number of significant bytes in your array
+   * @param destination
+   *          the array to hold the conversion
+   * @param destOffset
+   *          the index where output will be put
+   * @return the <var>destination</var> array
+   * @since 1.3
+   */
+  private static byte[] encode3to4(final byte[] source, final int srcOffset, final int numSigBytes,
+      final byte[] destination, final int destOffset) {
+    // 1 2 3
+    // 01234567890123456789012345678901 Bit position
+    // --------000000001111111122222222 Array position from threeBytes
+    // --------| || || || | Six bit groups to index ALPHABET
+    // >>18 >>12 >> 6 >> 0 Right shift necessary
+    // 0x3f 0x3f 0x3f Additional AND
+
+    // Create buffer with zero-padding if there are only one or two
+    // significant bytes passed in the array.
+    // We have to shift left 24 in order to flush out the 1's that appear
+    // when Java treats a value as negative that is cast from a byte to an
+    // int.
+    int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
+        | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
+        | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
+
+    switch (numSigBytes) {
+    case 3:
+      destination[destOffset] = ALPHABET[(inBuff >>> 18)];
+      destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
+      destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
+      destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
+      return destination;
+
+    case 2:
+      destination[destOffset] = ALPHABET[(inBuff >>> 18)];
+      destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
+      destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
+      destination[destOffset + 3] = EQUALS_SIGN;
+      return destination;
+
+    case 1:
+      destination[destOffset] = ALPHABET[(inBuff >>> 18)];
+      destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
+      destination[destOffset + 2] = EQUALS_SIGN;
+      destination[destOffset + 3] = EQUALS_SIGN;
+      return destination;
+
+    default:
+      return destination;
+    } // end switch
+  } // end encode3to4
+
+  /**
+   * Serializes an object and returns the Base64-encoded version of that
+   * serialized object. If the object cannot be serialized or there is another
+   * error, the method will return <tt>null</tt>. The object is not
+   * GZip-compressed before being encoded.
+   * 
+   * @param serializableObject
+   *          The object to encode
+   * @return The Base64-encoded object
+   * @since 1.4
+   */
+  public static String encodeObject(final java.io.Serializable serializableObject) {
+    return encodeObject(serializableObject, NO_OPTIONS);
+  } // end encodeObject
+
+  /**
+   * Serializes an object and returns the Base64-encoded version of that
+   * serialized object. If the object cannot be serialized or there is another
+   * error, the method will return <tt>null</tt>.
+   * <p>
+   * Valid options:
+   * 
+   * GZIP: gzip-compresses object before encoding it. DONT_BREAK_LINES: don't
+   * break lines at 76 characters &lt;i&gt;Note: Technically, this makes your
+   * encoding non-compliant.&lt;/i&gt;
+   * 
+   * <p>
+   * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
+   * <p>
+   * Example:
+   * <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+   * 
+   * @param serializableObject
+   *          The object to encode
+   * @param options
+   *          Specified options
+   * @return The Base64-encoded object
+   * @see Base64#GZIP
+   * @see Base64#DONT_BREAK_LINES
+   * @since 2.0
+   */
+  public static String encodeObject(final java.io.Serializable serializableObject, final int options) {
+    // Streams
+    java.io.ByteArrayOutputStream baos = null;
+    java.io.OutputStream b64os = null;
+    java.io.ObjectOutputStream oos = null;
+    java.util.zip.GZIPOutputStream gzos = null;
+
+    // Isolate options
+    int gzip = (options & GZIP);
+    int dontBreakLines = (options & DONT_BREAK_LINES);
+
+    try {
+      // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
+      baos = new java.io.ByteArrayOutputStream();
+      b64os = new Base64.Base64OutputStream(baos, ENCODE | dontBreakLines);
+
+      // GZip?
+      if (gzip == GZIP) {
+        gzos = new java.util.zip.GZIPOutputStream(b64os);
+        oos = new java.io.ObjectOutputStream(gzos);
+      } else {
+        oos = new java.io.ObjectOutputStream(b64os);
+      }
+
+      oos.writeObject(serializableObject);
+    } catch (java.io.IOException e) {
+      e.printStackTrace();
+      return null;
+    } finally {
+      try {
+        oos.close();
+      } catch (Exception e) {
+      }
+      try {
+        gzos.close();
+      } catch (Exception e) {
+      }
+      try {
+        b64os.close();
+      } catch (Exception e) {
+      }
+      try {
+        baos.close();
+      } catch (Exception e) {
+      }
+    } // end finally
+
+    // Return value according to relevant encoding.
+    try {
+      return new String(baos.toByteArray(), PREFERRED_ENCODING);
+    } catch (java.io.UnsupportedEncodingException uue) {
+      return new String(baos.toByteArray());
+    } // end catch
+
+  } // end encode
+
+  /**
+   * Encodes a byte array into Base64 notation. Does not GZip-compress data.
+   * 
+   * @param source
+   *          The data to convert
+   * @return the converted data as string
+   * @since 1.4
+   */
+  public static String encodeBytes(final byte[] source) {
+    return encodeBytes(source, 0, source.length, NO_OPTIONS);
+  } // end encodeBytes
+
+  /**
+   * Encodes a byte array into Base64 notation.
+   * <p>
+   * Valid options:
+   * 
+   * GZIP: gzip-compresses object before encoding it. DONT_BREAK_LINES: don't
+   * break lines at 76 characters &lt;i&gt;Note: Technically, this makes your
+   * encoding non-compliant.&lt;/i&gt;
+   * 
+   * <p>
+   * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+   * <p>
+   * Example:
+   * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+   * 
+   * @param source
+   *          The data to convert
+   * @param options
+   *          Specified options
+   * @return the converted data as string.
+   * @see Base64#GZIP
+   * @see Base64#DONT_BREAK_LINES
+   * @since 2.0
+   */
+  public static String encodeBytes(final byte[] source, final int options) {
+    return encodeBytes(source, 0, source.length, options);
+  } // end encodeBytes
+
+  /**
+   * Encodes a byte array into Base64 notation. Does not GZip-compress data.
+   * 
+   * @param source
+   *          The data to convert
+   * @param off
+   *          Offset in array where conversion should begin
+   * @param len
+   *          Length of data to convert
+   * @return the converted data as string.
+   * @since 1.4
+   */
+  public static String encodeBytes(final byte[] source, final int off, final int len) {
+    return encodeBytes(source, off, len, NO_OPTIONS);
+  } // end encodeBytes
+
+  /**
+   * Encodes a byte array into Base64 notation.
+   * <p>
+   * Valid options:
+   * 
+   * GZIP: gzip-compresses object before encoding it. DONT_BREAK_LINES: don't
+   * break lines at 76 characters &lt;i&gt;Note: Technically, this makes your
+   * encoding non-compliant.&lt;/i&gt;
+   * 
+   * <p>
+   * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+   * <p>
+   * Example:
+   * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+   * 
+   * @param source
+   *          The data to convert
+   * @param off
+   *          Offset in array where conversion should begin
+   * @param len
+   *          Length of data to convert
+   * @param options
+   *          Specified options
+   * @return the converted data as string.
+   * @see Base64#GZIP
+   * @see Base64#DONT_BREAK_LINES
+   * @since 2.0
+   */
+  public static String encodeBytes(final byte[] source, final int off, final int len, final int options) {
+    // Isolate options
+    int dontBreakLines = (options & DONT_BREAK_LINES);
+    int gzip = (options & GZIP);
+
+    // Compress?
+    if (gzip == GZIP) {
+      java.io.ByteArrayOutputStream baos = null;
+      java.util.zip.GZIPOutputStream gzos = null;
+      Base64.Base64OutputStream b64os = null;
+
+      try {
+        // GZip -> Base64 -> ByteArray
+        baos = new java.io.ByteArrayOutputStream();
+        b64os = new Base64.Base64OutputStream(baos, ENCODE | dontBreakLines);
+        gzos = new java.util.zip.GZIPOutputStream(b64os);
+
+        gzos.write(source, off, len);
+        gzos.close();
+      } catch (java.io.IOException e) {
+        e.printStackTrace();
+        return null;
+      } finally {
+        try {
+          gzos.close();
+        } catch (Exception e) {
+        }
+        try {
+          b64os.close();
+        } catch (Exception e) {
+        }
+        try {
+          baos.close();
+        } catch (Exception e) {
+        }
+      } // end finally
+
+      // Return value according to relevant encoding.
+      try {
+        return new String(baos.toByteArray(), PREFERRED_ENCODING);
+      } catch (java.io.UnsupportedEncodingException uue) {
+        return new String(baos.toByteArray());
+      } // end catch
+    } else {
+      // Else, don't compress. Better not to use streams at all then.
+      // Convert option to boolean in way that code likes it.
+      boolean breakLines = dontBreakLines == 0;
+
+      int len43 = len * 4 / 3;
+      byte[] outBuff = new byte[(len43) // Main 4:3
+          + ((len % 3) > 0 ? 4 : 0) // Account for padding
+          + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New
+      // lines
+      int d = 0;
+      int e = 0;
+      int len2 = len - 2;
+      int lineLength = 0;
+      for (; d < len2; d += 3, e += 4) {
+        encode3to4(source, d + off, 3, outBuff, e);
+
+        lineLength += 4;
+        if (breakLines && lineLength == MAX_LINE_LENGTH) {
+          outBuff[e + 4] = NEW_LINE;
+          e++;
+          lineLength = 0;
+        } // end if: end of line
+      } // en dfor: each piece of array
+
+      if (d < len) {
+        encode3to4(source, d + off, len - d, outBuff, e);
+        e += 4;
+      } // end if: some padding needed
+
+      // Return value according to relevant encoding.
+      try {
+        return new String(outBuff, 0, e, PREFERRED_ENCODING);
+      } catch (java.io.UnsupportedEncodingException uue) {
+        return new String(outBuff, 0, e);
+      } // end catch
+
+    } // end else: don't compress
+
+  } // end encodeBytes
+
+  /* ******** D E C O D I N G M E T H O D S ******** */
+
+  /**
+   * Decodes four bytes from array <var>source</var> and writes the resulting
+   * bytes (up to three of them) to <var>destination</var>. The source and
+   * destination arrays can be manipulated anywhere along their length by
+   * specifying <var>srcOffset</var> and <var>destOffset</var>. This method does
+   * not check to make sure your arrays are large enough to accomodate
+   * <var>srcOffset</var> + 4 for the <var>source</var> array or
+   * <var>destOffset</var> + 3 for the <var>destination</var> array. This method
+   * returns the actual number of bytes that were converted from the Base64
+   * encoding.
+   * 
+   * @param source
+   *          the array to convert
+   * @param srcOffset
+   *          the index where conversion begins
+   * @param destination
+   *          the array to hold the conversion
+   * @param destOffset
+   *          the index where output will be put
+   * @return the number of decoded bytes converted
+   * @since 1.3
+   */
+  private static int decode4to3(final byte[] source, final int srcOffset, final byte[] destination,
+      final int destOffset) {
+    // Example: Dk==
+    if (source[srcOffset + 2] == EQUALS_SIGN) {
+      // Two ways to do the same thing. Don't know which way I like best.
+      // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
+      // )
+      // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
+      int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
+
+      destination[destOffset] = (byte) (outBuff >>> 16);
+      return 1;
+    } else if (source[srcOffset + 3] == EQUALS_SIGN) {
+      // Example: DkL=
+      // Two ways to do the same thing. Don't know which way I like best.
+      // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
+      // )
+      // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+      // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
+      int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
+          | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
+
+      destination[destOffset] = (byte) (outBuff >>> 16);
+      destination[destOffset + 1] = (byte) (outBuff >>> 8);
+      return 2;
+    } else {
+      // Example: DkLE
+      try {
+        // Two ways to do the same thing. Don't know which way I like
+        // best.
+        // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 )
+        // >>> 6 )
+        // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+        // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
+        // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
+        int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
+            | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) | ((DECODABET[source[srcOffset + 3]] & 0xFF));
+
+        destination[destOffset] = (byte) (outBuff >> 16);
+        destination[destOffset + 1] = (byte) (outBuff >> 8);
+        destination[destOffset + 2] = (byte) (outBuff);
+
+        return 3;
+      } catch (Exception e) {
+        System.out.println("" + source[srcOffset] + ": " + (DECODABET[source[srcOffset]]));
+        System.out.println("" + source[srcOffset + 1] + ": " + (DECODABET[source[srcOffset + 1]]));
+        System.out.println("" + source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]]));
+        System.out.println("" + source[srcOffset + 3] + ": " + (DECODABET[source[srcOffset + 3]]));
+        return -1;
+      } // e nd catch
+    }
+  } // end decodeToBytes
+
+  /**
+   * Very low-level access to decoding ASCII characters in the form of a byte
+   * array. Does not support automatically gunzipping or any other "fancy"
+   * features.
+   * 
+   * @param source
+   *          The Base64 encoded data
+   * @param off
+   *          The offset of where to begin decoding
+   * @param len
+   *          The length of characters to decode
+   * @return decoded data
+   * @since 1.3
+   */
+  public static byte[] decode(final byte[] source, final int off, final int len) {
+    int len34 = len * 3 / 4;
+    byte[] outBuff = new byte[len34]; // Upper limit on size of output
+    int outBuffPosn = 0;
+
+    byte[] b4 = new byte[4];
+    int b4Posn = 0;
+    int i = 0;
+    byte sbiCrop = 0;
+    byte sbiDecode = 0;
+    for (i = off; i < off + len; i++) {
+      sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
+      sbiDecode = DECODABET[sbiCrop];
+
+      // White space, Equals sign or better
+      if (sbiDecode >= WHITE_SPACE_ENC) {
+        if (sbiDecode >= EQUALS_SIGN_ENC) {
+          b4[b4Posn++] = sbiCrop;
+          if (b4Posn > 3) {
+            outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
+            b4Posn = 0;
+
+            // If that was the equals sign, break out of 'for' loop
+            if (sbiCrop == EQUALS_SIGN) {
+              break;
+            }
+          } // end if: quartet built
+
+        } // end if: equals sign or better
+        // end if: white space, equals sign or better
+      } else {
+        System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
+        return null;
+      } // end else:
+    } // each input character
+
+    byte[] out = new byte[outBuffPosn];
+    System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
+    return out;
+  } // end decode
+
+  /**
+   * Decodes data from Base64 notation, automatically detecting gzip-compressed
+   * data and decompressing it.
+   * 
+   * @param s
+   *          the string to decode
+   * @return the decoded data
+   * @since 1.4
+   */
+  public static byte[] decode(final String s) {
+    byte[] bytes;
+    try {
+      bytes = s.getBytes(PREFERRED_ENCODING);
+    } catch (java.io.UnsupportedEncodingException uee) {
+      bytes = s.getBytes();
+    } // end catch
+    // </change>
+
+    // Decode
+    bytes = decode(bytes, 0, bytes.length);
+
+    // Check to see if it's gzip-compressed
+    // GZIP Magic Two-Byte Number: 0x8b1f (35615)
+    if (bytes != null && bytes.length >= 4) {
+
+      int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
+      if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
+        java.io.ByteArrayInputStream bais = null;
+        java.util.zip.GZIPInputStream gzis = null;
+        java.io.ByteArrayOutputStream baos = null;
+        byte[] buffer = new byte[2048];
+        int length = 0;
+
+        try {
+          baos = new java.io.ByteArrayOutputStream();
+          bais = new java.io.ByteArrayInputStream(bytes);
+          gzis = new java.util.zip.GZIPInputStream(bais);
+
+          while ((length = gzis.read(buffer)) >= 0) {
+            baos.write(buffer, 0, length);
+          } // end while: reading input
+
+          // No error? Get new bytes.
+          bytes = baos.toByteArray();
+
+        } catch (java.io.IOException e) {
+          // Just return originally-decoded bytes
+        } finally {
+          try {
+            baos.close();
+          } catch (Exception e) {
+          }
+          try {
+            gzis.close();
+          } catch (Exception e) {
+          }
+          try {
+            bais.close();
+          } catch (Exception e) {
+          }
+        } // end finally
+
+      } // end if: gzipped
+    } // end if: bytes.length >= 2
+
+    return bytes;
+  } // end decode
+
+  /**
+   * Attempts to decode Base64 data and deserialize a Java Object within.
+   * Returns <tt>null</tt> if there was an error.
+   * 
+   * @param encodedObject
+   *          The Base64 data to decode
+   * @return The decoded and deserialized object
+   * @since 1.5
+   */
+  public static Object decodeToObject(final String encodedObject) {
+    // Decode and gunzip if necessary
+    byte[] objBytes = decode(encodedObject);
+
+    java.io.ByteArrayInputStream bais = null;
+    java.io.ObjectInputStream ois = null;
+    Object obj = null;
+
+    try {
+      bais = new java.io.ByteArrayInputStream(objBytes);
+      ois = new java.io.ObjectInputStream(bais);
+
+      obj = ois.readObject();
+    } catch (java.io.IOException e) {
+      e.printStackTrace();
+      obj = null;
+    } catch (java.lang.ClassNotFoundException e) {
+      e.printStackTrace();
+      obj = null;
+    } finally {
+      try {
+        bais.close();
+      } catch (Exception e) {
+      }
+      try {
+        ois.close();
+      } catch (Exception e) {
+      }
+    } // end finally
+
+    return obj;
+  } // end decodeObject
+
+  /**
+   * Convenience method for encoding data to a file.
+   * 
+   * @param dataToEncode
+   *          byte array of data to encode in base64 form
+   * @param filename
+   *          Filename for saving encoded data
+   * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+   * @since 2.1
+   */
+  public static boolean encodeToFile(final byte[] dataToEncode, final String filename) {
+    boolean success = false;
+    Base64.Base64OutputStream bos = null;
+    try {
+      bos = new Base64.Base64OutputStream(new java.io.FileOutputStream(filename), Base64.ENCODE);
+      bos.write(dataToEncode);
+      success = true;
+    } catch (java.io.IOException e) {
+
+      success = false;
+    } finally {
+      try {
+        bos.close();
+      } catch (Exception e) {
+      }
+    } // end finally
+
+    return success;
+  } // end encodeToFile
+
+  /**
+   * Convenience method for decoding data to a file.
+   * 
+   * @param dataToDecode
+   *          Base64-encoded data as a string
+   * @param filename
+   *          Filename for saving decoded data
+   * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+   * @since 2.1
+   */
+  public static boolean decodeToFile(final String dataToDecode, final String filename) {
+    boolean success = false;
+    Base64.Base64OutputStream bos = null;
+    try {
+      bos = new Base64.Base64OutputStream(new java.io.FileOutputStream(filename), Base64.DECODE);
+      bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
+      success = true;
+    } catch (java.io.IOException e) {
+      success = false;
+    } finally {
+      try {
+        bos.close();
+      } catch (Exception e) {
+      }
+    } // end finally
+
+    return success;
+  } // end decodeToFile
+
+  /**
+   * Convenience method for reading a base64-encoded file and decoding it.
+   * 
+   * @param filename
+   *          Filename for reading encoded data
+   * @return decoded byte array or null if unsuccessful
+   * @since 2.1
+   */
+  public static byte[] decodeFromFile(final String filename) {
+    byte[] decodedData = null;
+    Base64.Base64InputStream bis = null;
+    try {
+      // Set up some useful variables
+      java.io.File file = new java.io.File(filename);
+      byte[] buffer = null;
+      int length = 0;
+      int numBytes = 0;
+
+      // Check for size of file
+      if (file.length() > Integer.MAX_VALUE) {
+        System.err.println("File is too big for this convenience method (" + file.length() + " bytes).");
+        return null;
+      } // end if: file too big for int index
+      buffer = new byte[(int) file.length()];
+
+      // Open a stream
+      bis = new Base64.Base64InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), Base64.DECODE);
+
+      // Read until done
+      while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
+        length += numBytes;
+      }
+
+      // Save in a variable to return
+      decodedData = new byte[length];
+      System.arraycopy(buffer, 0, decodedData, 0, length);
+
+    } catch (java.io.IOException e) {
+      System.err.println("Error decoding from file " + filename);
+    } finally {
+      try {
+        bis.close();
+      } catch (Exception e) {
+      }
+    } // end finally
+
+    return decodedData;
+  } // end decodeFromFile
+
+  /**
+   * Convenience method for reading a binary file and base64-encoding it.
+   * 
+   * @param filename
+   *          Filename for reading binary data
+   * @return base64-encoded string or null if unsuccessful
+   * @since 2.1
+   */
+  public static String encodeFromFile(final String filename) {
+    String encodedData = null;
+    Base64.Base64InputStream bis = null;
+    try {
+      // Set up some useful variables
+      java.io.File file = new java.io.File(filename);
+      byte[] buffer = new byte[(int) (file.length() * 1.4)];
+      int length = 0;
+      int numBytes = 0;
+
+      // Open a stream
+      bis = new Base64.Base64InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), Base64.ENCODE);
+
+      // Read until done
+      while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
+        length += numBytes;
+      }
+
+      // Save in a variable to return
+      encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING);
+
+    } catch (java.io.IOException e) {
+      System.err.println("Error encoding from file " + filename);
+    } finally {
+      try {
+        bis.close();
+      } catch (Exception e) {
+      }
+    } // end finally
+
+    return encodedData;
+  } // end encodeFromFile
+
+  /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
+
+  /**
+   * A {@link Base64.Base64InputStream} will read data from another
+   * <tt>java.io.InputStream</tt>, given in the constructor, and encode/decode
+   * to/from Base64 notation on the fly.
+   * 
+   * @see Base64
+   * @since 1.3
+   */
+  public static class Base64InputStream extends java.io.FilterInputStream {
+    /** Encoding or decoding. */
+    private boolean encode;
+
+    /** Current position in the buffer. */
+    private int position;
+
+    /** Small buffer holding converted data. */
+    private byte[] buffer;
+
+    /** Length of buffer (3 or 4). */
+    private int bufferLength;
+
+    /** Number of meaningful bytes in the buffer. */
+    private int numSigBytes;
+
+    /** length of the actual line. */
+    private int lineLength;
+
+    /** Break lines at less than 80 characters. */
+    private boolean breakLines;
+
+    /**
+     * Constructs a {@link Base64.Base64InputStream} in DECODE mode.
+     * 
+     * @param in
+     *          the <tt>java.io.InputStream</tt> from which to read data.
+     * @since 1.3
+     */
+    public Base64InputStream(final java.io.InputStream in) {
+      this(in, DECODE);
+    } // end constructor
+
+    /**
+     * Constructs a {@link Base64.Base64InputStream} in either ENCODE or DECODE mode.
+     * <p>
+     * Valid options:
+     * 
+     * ENCODE or DECODE: Encode or Decode as data is read. DONT_BREAK_LINES:
+     * don't break lines at 76 characters (only meaningful when encoding)
+     * &lt;i&gt;Note: Technically, this makes your encoding
+     * non-compliant.&lt;/i&gt;
+     * 
+     * <p>
+     * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
+     * 
+     * @param in
+     *          the <tt>java.io.InputStream</tt> from which to read data.
+     * @param options
+     *          Specified options
+     * @see Base64#ENCODE
+     * @see Base64#DECODE
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public Base64InputStream(final java.io.InputStream in, final int options) {
+      super(in);
+      this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+      this.encode = (options & ENCODE) == ENCODE;
+      this.bufferLength = encode ? 4 : 3;
+      this.buffer = new byte[bufferLength];
+      this.position = -1;
+      this.lineLength = 0;
+    } // end constructor
+
+    /**
+     * Reads enough of the input stream to convert to/from Base64 and returns
+     * the next byte.
+     * 
+     * @return next byte
+     * @throws java.io.IOException
+     *           if something goes wrong.
+     * @since 1.3
+     */
+    public final int read() throws java.io.IOException {
+      // Do we need to get data?
+      if (position < 0) {
+        if (encode) {
+          byte[] b3 = new byte[3];
+          int numBinaryBytes = 0;
+          for (int i = 0; i < 3; i++) {
+            try {
+              int b = in.read();
+
+              // If end of stream, b is -1.
+              if (b >= 0) {
+                b3[i] = (byte) b;
+                numBinaryBytes++;
+              } // end if: not end of stream
+
+            } catch (java.io.IOException e) {
+              // Only a problem if we got no data at all.
+              if (i == 0) {
+                throw e;
+              }
+
+            } // end catch
+          } // end for: each needed input byte
+
+          if (numBinaryBytes > 0) {
+            encode3to4(b3, 0, numBinaryBytes, buffer, 0);
+            position = 0;
+            numSigBytes = 4;
+          } else {
+            return -1;
+          } // end else
+        } else {
+          // Else decoding
+          byte[] b4 = new byte[4];
+          int i = 0;
+          for (i = 0; i < 4; i++) {
+            // Read four "meaningful" bytes:
+            int b = 0;
+            do {
+              b = in.read();
+            } while (b >= 0 && DECODABET[b & 0x7f] <= WHITE_SPACE_ENC);
+
+            if (b < 0) {
+              break; // Reads a -1 if end of stream
+            }
+
+            b4[i] = (byte) b;
+          } // end for: each needed input byte
+
+          if (i == 4) {
+            numSigBytes = decode4to3(b4, 0, buffer, 0);
+            position = 0;
+          } else if (i == 0) {
+            return -1;
+          } else {
+            // Must have broken out from above.
+            throw new java.io.IOException("Improperly padded Base64 input.");
+          } // end
+
+        } // end else: decode
+      } // end else: get data
+
+      // Got data?
+      if (position >= 0) {
+        // End of relevant data? (eleminated !encode &&
+        if (position >= numSigBytes) {
+          return -1;
+        }
+
+        if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
+          lineLength = 0;
+          return '\n';
+        } else {
+          lineLength++; // This isn't important when decoding
+          // but throwing an extra "if" seems
+          // just as wasteful.
+
+          int b = buffer[position++];
+
+          if (position >= bufferLength) {
+            position = -1;
+          }
+
+          return b & 0xFF; // This is how you "cast" a byte that's
+          // intended to be unsigned.
+        } // end else
+      } else {
+        // Else error
+        // When JDK1.4 is more accepted, use an assertion here.
+        throw new java.io.IOException("Error in Base64 code reading stream.");
+      } // end else
+    } // end read
+
+    /**
+     * Calls {@link #read()} repeatedly until the end of stream is reached or
+     * <var>len</var> bytes are read. Returns number of bytes read into array or
+     * -1 if end of stream is encountered.
+     * 
+     * @param dest
+     *          array to hold values
+     * @param off
+     *          offset for array
+     * @param len
+     *          max number of bytes to read into array
+     * @return bytes read into array or -1 if end of stream is encountered.
+     * @throws java.io.IOException
+     *           if something goes wrong.
+     * @since 1.3
+     */
+    public final int read(final byte[] dest, final int off, final int len) throws java.io.IOException {
+      int i;
+      int b;
+      for (i = 0; i < len; i++) {
+        b = read();
+
+        // if( b < 0 && i == 0 )
+        // return -1;
+
+        if (b >= 0) {
+          dest[off + i] = (byte) b;
+        } else if (i == 0) {
+          return -1;
+        } else {
+          break; // Out of 'for' loop
+        }
+      } // end for: each byte read
+      return i;
+    } // end read
+
+  } // end inner class InputStream
+
+  /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
+
+  /**
+   * A {@link Base64.Base64OutputStream} will write data to another
+   * <tt>java.io.OutputStream</tt>, given in the constructor, and encode/decode
+   * to/from Base64 notation on the fly.
+   * 
+   * @see Base64
+   * @since 1.3
+   */
+  public static class Base64OutputStream extends java.io.FilterOutputStream {
+    /** Encoding or decoding. */
+    private boolean encode;
+
+    /** Current position in the buffer. */
+    private int position;
+
+    /** Small buffer holding converted data. */
+    private byte[] buffer;
+
+    /** Length of buffer (3 or 4). */
+    private int bufferLength;
+
+    /** length of the actual line. */
+    private int lineLength;
+
+    /** Break lines at less than 80 characters. */
+    private boolean breakLines;
+
+    /** Scratch used in a few places. */
+    private byte[] b4;
+
+    /** just don't do encodign now. */
+    private boolean suspendEncoding;
+
+    /**
+     * Constructs a {@link Base64.Base64OutputStream} in ENCODE mode.
+     * 
+     * @param out
+     *          the <tt>java.io.OutputStream</tt> to which data will be written.
+     * @since 1.3
+     */
+    public Base64OutputStream(final java.io.OutputStream out) {
+      this(out, ENCODE);
+    } // end constructor
+
+    /**
+     * Constructs a {@link Base64.Base64OutputStream} in either ENCODE or DECODE mode.
+     * <p>
+     * Valid options:
+     * 
+     * ENCODE or DECODE: Encode or Decode as data is read. DONT_BREAK_LINES:
+     * don't break lines at 76 characters (only meaningful when encoding)
+     * &lt;i&gt;Note: Technically, this makes your encoding
+     * non-compliant.&lt;/i&gt;
+     * 
+     * <p>
+     * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
+     * 
+     * @param out
+     *          the <tt>java.io.OutputStream</tt> to which data will be written.
+     * @param options
+     *          Specified options.
+     * @see Base64#ENCODE
+     * @see Base64#DECODE
+     * @see Base64#DONT_BREAK_LINES
+     * @since 1.3
+     */
+    public Base64OutputStream(final java.io.OutputStream out, final int options) {
+      super(out);
+      this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+      this.encode = (options & ENCODE) == ENCODE;
+      this.bufferLength = encode ? 3 : 4;
+      this.buffer = new byte[bufferLength];
+      this.position = 0;
+      this.lineLength = 0;
+      this.suspendEncoding = false;
+      this.b4 = new byte[4];
+    } // end constructor
+
+    /**
+     * Writes the byte to the output stream after converting to/from Base64
+     * notation. When encoding, bytes are buffered three at a time before the
+     * output stream actually gets a write() call. When decoding, bytes are
+     * buffered four at a time.
+     * 
+     * @param theByte
+     *          the byte to write
+     * @throws java.io.IOException
+     *           if something goes wrong.
+     * @since 1.3
+     */
+    public final void write(final int theByte) throws java.io.IOException {
+      // Encoding suspended?
+      if (suspendEncoding) {
+        super.out.write(theByte);
+        return;
+      } // end if: supsended
+
+      // Encode?
+      if (encode) {
+        buffer[position++] = (byte) theByte;
+        if (position >= bufferLength) {
+          // Enough to encode.
+          out.write(encode3to4(b4, buffer, bufferLength));
+
+          lineLength += 4;
+          if (breakLines && lineLength >= MAX_LINE_LENGTH) {
+            out.write(NEW_LINE);
+            lineLength = 0;
+          } // end if: end of line
+
+          position = 0;
+        } // end if: enough to output
+      } else {
+        // Else, Decoding
+        // Meaningful Base64 character?
+        if (DECODABET[theByte & 0x7f] > WHITE_SPACE_ENC) {
+          buffer[position++] = (byte) theByte;
+          if (position >= bufferLength) {
+            // Enough to output.
+            int len = Base64.decode4to3(buffer, 0, b4, 0);
+            out.write(b4, 0, len);
+            // out.write( Base64.decode4to3( buffer ) );
+            position = 0;
+          } // end if: enough to output
+          // end if: meaningful base64 character
+        } else if (DECODABET[theByte & 0x7f] != WHITE_SPACE_ENC) {
+          throw new java.io.IOException("Invalid character in Base64 data.");
+        } // end else: not white space either
+      } // end else: decoding
+    } // end write
+
+    /**
+     * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are
+     * written.
+     * 
+     * @param theBytes
+     *          array from which to read bytes
+     * @param off
+     *          offset for array
+     * @param len
+     *          max number of bytes to read into array
+     * @throws java.io.IOException
+     *           if something goes wrong.
+     * @since 1.3
+     */
+    public final void write(final byte[] theBytes, final int off, final int len) throws java.io.IOException {
+      // Encoding suspended?
+      if (suspendEncoding) {
+        super.out.write(theBytes, off, len);
+        return;
+      } // end if: supsended
+
+      for (int i = 0; i < len; i++) {
+        write(theBytes[off + i]);
+      } // end for: each byte written
+
+    } // end write
+
+    /**
+     * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer without
+     * closing the stream.
+     * 
+     * @throws java.io.IOException
+     *           if something goes wrong.
+     */
+    public final void flushBase64() throws java.io.IOException {
+      if (position > 0) {
+        if (encode) {
+          out.write(encode3to4(b4, buffer, position));
+          position = 0;
+        } else {
+          throw new java.io.IOException("Base64 input not properly padded.");
+        } // end else: decoding
+      } // end if: buffer partially full
+
+    } // end flush
+
+    /**
+     * Flushes and closes (I think, in the superclass) the stream.
+     * 
+     * @throws java.io.IOException
+     *           if something goes wrong.
+     * @since 1.3
+     */
+    public final void close() throws java.io.IOException {
+      // 1. Ensure that pending characters are written
+      flushBase64();
+
+      // 2. Actually close the stream
+      // Base class both flushes and closes.
+      super.close();
+
+      buffer = null;
+      out = null;
+    } // end close
+
+    /**
+     * Suspends encoding of the stream. May be helpful if you need to embed a
+     * piece of base640-encoded data in a stream.
+     * 
+     * @throws java.io.IOException
+     *           if something goes wrong.
+     * @since 1.5.1
+     */
+    public final void suspendEncoding() throws java.io.IOException {
+      flushBase64();
+      this.suspendEncoding = true;
+    } // end suspendEncoding
+
+    /**
+     * Resumes encoding of the stream. May be helpful if you need to embed a
+     * piece of base640-encoded data in a stream.
+     * 
+     * @since 1.5.1
+     */
+    public final void resumeEncoding() {
+      this.suspendEncoding = false;
+    } // end resumeEncoding
+
+  } // end inner class OutputStream
+
+} // end class Base64

+ 342 - 0
src/main/java/de/mcs/utils/codecs/Base64Decoder.java

@@ -0,0 +1,342 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils.codecs;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Decode a BASE64 encoded input stream to some output stream. This class
+ * implements BASE64 decoding, as specified in the <a
+ * href="http://ds.internic.net/rfc/rfc1521.txt">MIME specification </a>. see
+ * org.w3c.tools.codec.Base64Encoder This class is a modified version based on
+ * code obtained from the w3 consortium website, which is subject to their
+ * generic copyright notice:
+ * <dl>
+ * <dd><a href="http://www.w3.org/Consortium/Legal/">Copyright </a>�
+ * [$date-of-software] <a HREF="http://www.w3.org/">World Wide Web Consortium
+ * </a>, ( <a HREF="http://www.lcs.mit.edu/">Massachusetts Institute of
+ * Technology </a>, <a HREF="http://www.inria.fr/">Institut National de
+ * Recherche en Informatique et en Automatique </a>, <a
+ * HREF="http://www.keio.ac.jp/">Keio University </a>). All Rights Reserved.
+ * This program is distributed under the <a
+ * HREF="http://www.w3.org/Consortium/Legal/copyright-software-19980720.html">W3C's
+ * Software Intellectual Property License </a>. This program is distributed in
+ * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * W3C License <a
+ * href="http://www.w3.org/Consortium/Legal/">http://www.w3.org/Consortium/Legal/
+ * </a> for more details.</dd>
+ * </dl>
+ * 
+ * @version $Revision: 1.1 $
+ * @deprecated please use the base64 class.
+ */
+public class Base64Decoder {
+  /** Größe des puffers. */
+  private static final int BUFFER_SIZE = 1024;
+
+  /** encoding. */
+  private String encoding = null;
+
+  /** Field in. */
+  private InputStream in = null;
+
+  /** Field out. */
+  private OutputStream out = null;
+
+  /** Field stringp. */
+  private boolean stringp = false;
+
+  // private void printHex(int x) {
+  // int h = (x & 0xf0) >> 4;
+  // int l = (x & 0x0f);
+  // System.out.print(
+  // (new Character((char) ((h > 9) ? 'A' + h - 10 : '0' + h))).toString()
+  // + (new Character((char) ((l > 9) ? 'A' + l - 10 : '0' + l))).toString());
+  // }
+
+  // private void printHex(byte buf[], int off, int len) {
+  // while (off < len) {
+  // printHex(buf[off++]);
+  // System.out.print(" ");
+  // }
+  // System.out.println("");
+  // }
+
+  // private void printHex(String s) {
+  // byte bytes[];
+  // try {
+  // bytes = s.getBytes("ISO-8859-1");
+  // }
+  // catch (UnsupportedEncodingException ex) {
+  // throw new RuntimeException(
+  // this.getClass().getName() + "[printHex] Unable to convert" + "properly
+  // char to bytes");
+  // }
+  // printHex(bytes, 0, bytes.length);
+  // }
+
+  /**
+   * decode mime string to byte array.
+   * 
+   * @param string
+   *            the string to decode
+   * @return byte[]
+   * @throws Exception
+   *             if something goes wrong
+   */
+  public static byte[] decode(final String string) throws Exception {
+    ByteArrayInputStream bin = new ByteArrayInputStream(string.getBytes());
+    ByteArrayOutputStream bout = new ByteArrayOutputStream();
+    Base64Decoder myDecode = new Base64Decoder(bin, bout);
+    myDecode.process();
+    return bout.toByteArray();
+  }
+
+  /**
+   * @param buf
+   *            der Puffer
+   * @param off
+   *            der Offset
+   * @return int das Zeichen
+   */
+  private int get1(final byte[] buf, final int off) {
+    return ((buf[off] & 0x3f) << 2) | ((buf[off + 1] & 0x30) >>> 4);
+  }
+
+  /**
+   * @param buf
+   *            der Puffer
+   * @param off
+   *            der Offset
+   * @return int das Zeichen
+   */
+  private int get2(final byte[] buf, final int off) {
+    return ((buf[off + 1] & 0x0f) << 4) | ((buf[off + 2] & 0x3c) >>> 2);
+  }
+
+  /**
+   * @param buf
+   *            der Puffer
+   * @param off
+   *            der Offset
+   * @return int das Zeichen
+   */
+  private int get3(final byte[] buf, final int off) {
+    return ((buf[off + 2] & 0x03) << 6) | (buf[off + 3] & 0x3f);
+  }
+
+  /**
+   * check something.
+   * 
+   * @param ch
+   *            input character
+   * @return int
+   */
+  private int check(final int ch) {
+    if ((ch >= 'A') && (ch <= 'Z')) {
+      return ch - 'A';
+    } else if ((ch >= 'a') && (ch <= 'z')) {
+      return ch - 'a' + 26;
+    } else if ((ch >= '0') && (ch <= '9')) {
+      return ch - '0' + 52;
+    } else {
+      switch (ch) {
+      case '=':
+        return 65;
+      case '+':
+        return 62;
+      case '/':
+        return 63;
+      default:
+        return -1;
+      }
+    }
+  }
+
+  /**
+   * Do the actual decoding. Process the input stream by decoding it and
+   * emiting the resulting bytes into the output stream. exception IOException
+   * If the input or output stream accesses failed.
+   * 
+   * @exception Exception
+   *                If the input stream is not compliant with the BASE64
+   *                specification.
+   */
+
+  public final void process() throws Exception {
+    byte[] buffer = new byte[BUFFER_SIZE];
+    byte[] chunk = new byte[4];
+    int got = -1;
+    int ready = 0;
+
+    fill: while ((got = in.read(buffer)) > 0) {
+      int skiped = 0;
+      while (skiped < got) {
+        // Check for un-understood characters:
+        while (ready < 4) {
+          if (skiped >= got) {
+            continue fill;
+          }
+          int ch = check(buffer[skiped++]);
+          if (ch >= 0) {
+            chunk[ready++] = (byte) ch;
+          }
+        }
+        if (chunk[2] == 65) {
+          out.write(get1(chunk, 0));
+          return;
+        } else if (chunk[3] == 65) {
+          out.write(get1(chunk, 0));
+          out.write(get2(chunk, 0));
+          return;
+        } else {
+          out.write(get1(chunk, 0));
+          out.write(get2(chunk, 0));
+          out.write(get3(chunk, 0));
+        }
+        ready = 0;
+      }
+    }
+    if (ready != 0) {
+      throw new Exception("Invalid length.");
+    }
+    out.flush();
+  }
+
+  /**
+   * Do the decoding, and return a String. This methods should be called when
+   * the decoder is used in <em>String</em> mode. It decodes the input
+   * string to an output string that is returned.
+   * 
+   * @return String exception RuntimeException If the object wasn't
+   *         constructed to decode a String.
+   */
+
+  public final String processString() {
+    if (!stringp) {
+      throw new RuntimeException(this.getClass().getName() + "[processString]" + "invalid call (not a String)");
+    }
+    try {
+      process();
+    } catch (IOException e) {
+      e.printStackTrace();
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+    String s;
+    try {
+      s = ((ByteArrayOutputStream) out).toString(encoding);
+    } catch (Exception ex) {
+      throw new RuntimeException(this.getClass().getName() + "[processString] Unable to convert"
+          + "properly char to bytes");
+    }
+    return s;
+  }
+
+  /**
+   * Constructor for Base64Decoder.
+   * 
+   * @param input
+   *            String
+   */
+  public Base64Decoder(final String input) {
+    this(input, null);
+  }
+
+  /**
+   * Create a decoder to decode a String.
+   * 
+   * @param input
+   *            The string to be decoded.
+   * @param sEncoding
+   *            String
+   */
+
+  public Base64Decoder(final String input, final String sEncoding) {
+    byte[] bytes;
+    String myEncoding = sEncoding;
+    if (myEncoding == null) {
+      myEncoding = "ISO-8859-1";
+    }
+
+    try {
+      bytes = input.getBytes(encoding);
+    } catch (UnsupportedEncodingException ex) {
+      throw new RuntimeException(this.getClass().getName() + "[Constructor] Unable to convert"
+          + "properly char to bytes");
+    }
+    this.stringp = true;
+    this.in = new ByteArrayInputStream(bytes);
+    this.encoding = myEncoding;
+
+    this.out = new ByteArrayOutputStream();
+  }
+
+  /**
+   * Create a decoder to decode a stream.
+   * 
+   * @param isIn
+   *            The input stream (to be decoded).
+   * @param isOut
+   *            The output stream, to write decoded data to.
+   */
+
+  public Base64Decoder(final InputStream isIn, final OutputStream isOut) {
+    this.in = isIn;
+    this.out = isOut;
+    this.stringp = false;
+  }
+
+  /**
+   * Test the decoder. Run it with one argument: the string to be decoded, it
+   * will print out the decoded value.
+   * 
+   * @param args
+   *            String[]
+   */
+
+  public static void main(final String[] args) {
+    if (args.length == 1) {
+      try {
+        Base64Decoder b = new Base64Decoder(args[0]);
+        System.out.println("[" + b.processString() + "]");
+      } catch (RuntimeException e) {
+        System.out.println("Invalid Base64 format !");
+        System.exit(1);
+      }
+    } else if ((args.length == 2) && (args[0].equals("-f"))) {
+      try {
+        FileInputStream in = new FileInputStream(args[1]);
+        Base64Decoder b = new Base64Decoder(in, System.out);
+        b.process();
+      } catch (Exception ex) {
+        System.out.println("error: " + ex.getMessage());
+        System.exit(1);
+      }
+    } else {
+      System.out.println("Base64Decoder [strong] [-f file]");
+    }
+    System.exit(0);
+  }
+}

+ 319 - 0
src/main/java/de/mcs/utils/codecs/Base64Encoder.java

@@ -0,0 +1,319 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2005 by MCS
+ * -------------------------------------- Created on 16.01.2004 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.mcs.utils.codecs;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * BASE64 encoder implementation. This object takes as parameter an input stream
+ * and an output stream. It encodes the input stream, using the BASE64 ENCODING
+ * rules, as defined in <a href="http://ds.internic.net/rfc/rfc1521.txt">MIME
+ * specification </a> and emit the resulting data to the output stream. see
+ * org.w3c.tools.codec.Base64Decoder This class is a modified version based on
+ * code obtained from the w3 consortium website, which is subject to their
+ * generic copyright notice:
+ * <dl>
+ * <dd><a href="http://www.w3.org/Consortium/Legal/">Copyright </a>
+ * [$date-of-software] <a HREF="http://www.w3.org/">World Wide Web Consortium
+ * </a>, ( <a HREF="http://www.lcs.mit.edu/">Massachusetts Institute of
+ * Technology </a>, <a HREF="http://www.inria.fr/">Institut National de
+ * Recherche en Informatique et en Automatique </a>, <a
+ * HREF="http://www.keio.ac.jp/">Keio University </a>). All Rights Reserved.
+ * This program is distributed under the <a
+ * HREF="http://www.w3.org/Consortium/Legal/copyright-software-19980720.html">W3C's
+ * Software Intellectual Property License </a>. This program is distributed in
+ * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * W3C License <a
+ * href="http://www.w3.org/Consortium/Legal/">http://www.w3.org/Consortium/Legal/
+ * </a> for more details.</dd>
+ * </dl>
+ * 
+ * @version $Revision: 1.1 $
+ * @deprecated please use the base64 class.
+ */
+
+public final class Base64Encoder {
+  /** internal buffer size for ENCODING. */
+  private static final int BUFFER_SIZE = 1024;
+
+  /** encoding byte array. */
+  private static final byte[] ENCODING = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
+      (byte) 'G', (byte) 'H', // 0-7
+      (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', // 8-15
+      (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', // 16-23
+      (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', // 24-31
+      (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', // 32-39
+      (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', // 40-47
+      (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', // 48-55
+      (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/', // 56-63
+      (byte) '=' // 64
+  };
+
+  /** to prevent instancing. */
+  private Base64Encoder() {
+  }
+
+  /**
+   * Encodes data from supplied input to output.
+   * 
+   * @param in
+   *            The input stream to be encoded.
+   * @param out
+   *            The output stream to write encoded data to.
+   * @throws IOException
+   *             if something goes wrong
+   */
+  public static void encode(final InputStream in, final OutputStream out) throws IOException {
+    process(in, out);
+  }
+
+  /**
+   * Encodes from the supplied byte array and write the encoded data to the
+   * OutputStream <i>out </i>.
+   * 
+   * @param input
+   *            The byte array input to be encoded.
+   * @param out
+   *            The output stream to write encoded data to.
+   * @throws IOException
+   *             if something goes wrong
+   */
+  public static void encode(final byte[] input, final OutputStream out) throws IOException {
+    ByteArrayInputStream in = new ByteArrayInputStream(input);
+    process(in, out);
+  }
+
+  /**
+   * Encode the given string and return the encoded version as a string.
+   * 
+   * @param input
+   *            The string input to be encoded. It is assumed the string input
+   *            uses characters from the ISO 8859-1 code page.
+   * @return A String, representing the encoded content of the input String.
+   *         The returned string uses charactes from 8859-1 code page.
+   * @throws IOException
+   *             if something goes wrong
+   */
+  public static String encode(final String input) throws IOException {
+    byte[] bytes;
+    bytes = input.getBytes("ISO-8859-1");
+    return encode(bytes);
+  }
+
+  /**
+   * Encode the given byte array and return the encoded version as a string.
+   * 
+   * @param bytes
+   *            The byte array to be encoded.
+   * @return A String, representing the encoded content of the input bytes.
+   *         The returned string uses charactes from 8859-1 code page.
+   * @throws IOException
+   *             if something goes wrong
+   */
+  public static String encode(final byte[] bytes) throws IOException {
+    ByteArrayInputStream in = new ByteArrayInputStream(bytes);
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    process(in, out);
+    return out.toString("ISO-8859-1");
+  }
+
+  /**
+   * Run with one argument, prints the encoded version of it. With two, the
+   * second is assumed to be the name of a MessageDigest to be applied to the
+   * string before ENCODING (useful for generating password hashes).
+   * <p>
+   * Alternatively, use the openssl utility, for example:
+   * <p>
+   * echo -n "password" | openssl dgst -sha1 -binary | openssl base64
+   * 
+   * @param args
+   *            String[]
+   * @throws Exception
+   *             if something goes wrong
+   */
+  public static void main(final String[] args) throws Exception {
+    if (args.length == 1) {
+      System.out.println("[" + Base64Encoder.encode(args[0]) + "]");
+      // joe:eoj -> am9lOmVvag==
+      // 12345678:87654321 -> MTIzNDU2Nzg6ODc2NTQzMjE=
+    } else if (args.length == 2) {
+      byte[] hash = java.security.MessageDigest.getInstance(args[1]).digest(args[0].getBytes());
+      System.out.println("[" + Base64Encoder.encode(hash) + "]");
+    } else {
+      System.out.println("Usage: Base64Encoder <string> <optional hash algorithm>");
+    }
+  }
+
+  // Private ----------------------------------------------------------------
+  /**
+   * getting the first integer.
+   * 
+   * @param buf
+   *            byte array
+   * @param off
+   *            offset
+   * @return int
+   */
+  private static int get1(final byte[] buf, final int off) {
+    return (buf[off] & 0xfc) >> 2;
+  }
+
+  /**
+   * getting the second integer.
+   * 
+   * @param buf
+   *            byte array
+   * @param off
+   *            offset
+   * @return int
+   */
+  private static int get2(final byte[] buf, final int off) {
+    return ((buf[off] & 0x3) << 4) | ((buf[off + 1] & 0xf0) >>> 4);
+  }
+
+  /**
+   * getting the third integer.
+   * 
+   * @param buf
+   *            byte array
+   * @param off
+   *            offset
+   * @return int
+   */
+  private static int get3(final byte[] buf, final int off) {
+    return ((buf[off + 1] & 0x0f) << 2) | ((buf[off + 2] & 0xc0) >>> 6);
+  }
+
+  /**
+   * getting the forth integer.
+   * 
+   * @param buf
+   *            byte array
+   * @param off
+   *            offset
+   * @return int
+   */
+  private static int get4(final byte[] buf, final int off) {
+    return buf[off + 2] & 0x3f;
+  }
+
+  /**
+   * Process the data: encode the input stream to the output stream. This
+   * method runs through the input stream, ENCODING it to the output stream.
+   * 
+   * @param in
+   *            input stream
+   * @param out
+   *            output stream
+   * @exception IOException
+   *                If we weren't able to access the input stream or the
+   *                output stream.
+   */
+  private static void process(final InputStream in, final OutputStream out) throws IOException {
+    byte[] buffer = new byte[BUFFER_SIZE];
+    int got = -1;
+    int off = 0;
+    int count = 0;
+    while ((got = in.read(buffer, off, BUFFER_SIZE - off)) > 0) {
+      if (got >= 3) {
+        got += off;
+        off = 0;
+        while (off + 3 <= got) {
+          int c1 = get1(buffer, off);
+          int c2 = get2(buffer, off);
+          int c3 = get3(buffer, off);
+          int c4 = get4(buffer, off);
+          switch (count) {
+          case 73:
+            out.write(ENCODING[c1]);
+            out.write(ENCODING[c2]);
+            out.write(ENCODING[c3]);
+            out.write('\n');
+            out.write(ENCODING[c4]);
+            count = 1;
+            break;
+          case 74:
+            out.write(ENCODING[c1]);
+            out.write(ENCODING[c2]);
+            out.write('\n');
+            out.write(ENCODING[c3]);
+            out.write(ENCODING[c4]);
+            count = 2;
+            break;
+          case 75:
+            out.write(ENCODING[c1]);
+            out.write('\n');
+            out.write(ENCODING[c2]);
+            out.write(ENCODING[c3]);
+            out.write(ENCODING[c4]);
+            count = 3;
+            break;
+          case 76:
+            out.write('\n');
+            out.write(ENCODING[c1]);
+            out.write(ENCODING[c2]);
+            out.write(ENCODING[c3]);
+            out.write(ENCODING[c4]);
+            count = 4;
+            break;
+          default:
+            out.write(ENCODING[c1]);
+            out.write(ENCODING[c2]);
+            out.write(ENCODING[c3]);
+            out.write(ENCODING[c4]);
+            count += 4;
+            break;
+          }
+          off += 3;
+        }
+        // Copy remaining bytes to beginning of buffer:
+        for (int i = 0; i < 3; i++) {
+          if (i < got - off) {
+            buffer[i] = buffer[off + i];
+          } else {
+            buffer[i] = ((byte) 0);
+          }
+        }
+        off = got - off;
+      } else {
+        // Total read amount is less then 3 bytes:
+        off += got;
+      }
+    }
+    // Manage the last bytes, from 0 to off:
+    switch (off) {
+    case 1:
+      out.write(ENCODING[get1(buffer, 0)]);
+      out.write(ENCODING[get2(buffer, 0)]);
+      out.write('=');
+      out.write('=');
+      break;
+    case 2:
+      out.write(ENCODING[get1(buffer, 0)]);
+      out.write(ENCODING[get2(buffer, 0)]);
+      out.write(ENCODING[get3(buffer, 0)]);
+      out.write('=');
+    default:
+    }
+    return;
+  }
+}

+ 29 - 0
src/main/java/de/mcs/utils/event/AbstractEvent.java

@@ -0,0 +1,29 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2012 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: AbstractEvent.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 12.03.2012 Willie
+ */
+
+package de.mcs.utils.event;
+
+/**
+ * @author Willie
+ *
+ */
+public class AbstractEvent implements Event {
+
+  private String name;
+
+  public AbstractEvent(final String name) {
+    this.name = name;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+}

+ 143 - 0
src/main/java/de/mcs/utils/event/CallbackList.java

@@ -0,0 +1,143 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2012 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: CallbackList.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 25.03.2012 Willie
+ */
+
+package de.mcs.utils.event;
+
+import java.lang.ref.WeakReference;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author Willie
+ *
+ */
+public class CallbackList<E> extends AbstractList<E> implements List<E> {
+  public enum Operation {
+    add, addall, remove, clear
+  };
+
+  public interface ListCallback {
+    void handle(Operation operation);
+  }
+
+  private List<WeakReference<ListCallback>> callbackList = new ArrayList<WeakReference<ListCallback>>();
+
+  private List<E> list = null;
+
+  public CallbackList() {
+    this.list = new ArrayList<E>();
+  }
+
+  public CallbackList(List<E> list) {
+    this.list = list;
+  }
+
+  public void addCallback(final ListCallback listCallback) {
+    for (Iterator<WeakReference<ListCallback>> iter = callbackList.iterator(); iter.hasNext();) {
+      WeakReference<ListCallback> listener = iter.next();
+      if (listener.isEnqueued()) {
+        iter.remove();
+      } else {
+        if (listCallback.equals(listener.get())) {
+          return;
+        }
+      }
+    }
+    WeakReference<ListCallback> weakEventListener = new WeakReference<ListCallback>(listCallback);
+    callbackList.add(weakEventListener);
+  }
+
+  public void removeCallback(final ListCallback listCallback) {
+    for (Iterator<WeakReference<ListCallback>> iter = callbackList.iterator(); iter.hasNext();) {
+      WeakReference<ListCallback> listener = iter.next();
+      if (listener.isEnqueued()) {
+        iter.remove();
+      } else {
+        if (listCallback.equals(listener.get())) {
+          iter.remove();
+        }
+      }
+    }
+  }
+
+  @Override
+  public E get(int index) {
+    return list.get(index);
+  }
+
+  @Override
+  public int size() {
+    return list.size();
+  }
+
+  /* (non-Javadoc)
+   * @see java.util.AbstractList#add(int, java.lang.Object)
+   */
+  @Override
+  public void add(int index, E element) {
+    list.add(index, element);
+    fireCallback(Operation.add);
+  }
+
+  public void fireCallback(Operation operation) {
+    for (Iterator<WeakReference<ListCallback>> iter = callbackList.iterator(); iter.hasNext();) {
+      WeakReference<ListCallback> weakListener = iter.next();
+      if (weakListener.isEnqueued()) {
+        iter.remove();
+      } else if (weakListener.get() == null) {
+        iter.remove();
+      } else {
+        weakListener.get().handle(operation);
+      }
+    }
+  }
+
+  /* (non-Javadoc)
+   * @see java.util.AbstractList#remove(int)
+   */
+  @Override
+  public E remove(int index) {
+    E remove = list.remove(index);
+    fireCallback(Operation.remove);
+    return remove;
+  }
+
+  /* (non-Javadoc)
+   * @see java.util.AbstractList#clear()
+   */
+  @Override
+  public void clear() {
+    list.clear();
+    fireCallback(Operation.clear);
+  }
+
+  /* (non-Javadoc)
+   * @see java.util.AbstractList#addAll(int, java.util.Collection)
+   */
+  @Override
+  public boolean addAll(int index, Collection<? extends E> c) {
+    boolean addAll = list.addAll(index, c);
+    fireCallback(Operation.addall);
+    return addAll;
+  }
+
+  /* (non-Javadoc)
+   * @see java.util.AbstractCollection#addAll(java.util.Collection)
+   */
+  @Override
+  public boolean addAll(Collection<? extends E> c) {
+    boolean addAll = list.addAll(c);
+    fireCallback(Operation.addall);
+    return addAll;
+  }
+
+}

+ 32 - 0
src/main/java/de/mcs/utils/event/Delegate.java

@@ -0,0 +1,32 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2012 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: Delegate.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 14.03.2012 Willie
+ */
+
+package de.mcs.utils.event;
+
+/**
+ * @author Willie
+ *
+ */
+public interface Delegate {
+
+  void registerEvent(Event event);
+
+  void removeAllListeners(Event event);
+
+  boolean isListenerRegistered(Event event, EventListener eventListener);
+
+  boolean isEventRegistered(Event event1);
+
+  void fireEvent(Event event, Object... data);
+
+  void unregisterListener(Event event, EventListener eventListener);
+
+  void registerListener(Event event, EventListener eventListener);
+
+}

+ 158 - 0
src/main/java/de/mcs/utils/event/DelegateImpl.java

@@ -0,0 +1,158 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2012 by Wilfried Klaas
+ * Project: SmallArchivePivotGUI
+ * File: EventFactory.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 11.03.2012 Willie
+ */
+
+package de.mcs.utils.event;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Willie
+ *
+ */
+public class DelegateImpl implements Delegate {
+
+  private Map<String, List<WeakReference<EventListener>>> events;
+
+  public DelegateImpl() {
+    this.events = new HashMap<String, List<WeakReference<EventListener>>>();
+  }
+
+  @Override
+  public void registerEvent(Event event) {
+    synchronized (events) {
+      if (!events.containsKey(event.getName())) {
+        events.put(event.getName(), new ArrayList<WeakReference<EventListener>>());
+      }
+    }
+  }
+
+  @Override
+  public void registerListener(Event event, EventListener eventListener) {
+    List<WeakReference<EventListener>> list = null;
+    synchronized (events) {
+      if (events.containsKey(event.getName())) {
+        list = events.get(event.getName());
+      }
+    }
+    if (list != null) {
+      synchronized (list) {
+        for (Iterator<WeakReference<EventListener>> iter = list.iterator(); iter.hasNext();) {
+          WeakReference<EventListener> listener = iter.next();
+          if (listener.isEnqueued()) {
+            iter.remove();
+          } else {
+            if (eventListener.equals(listener.get())) {
+              return;
+            }
+          }
+        }
+        WeakReference<EventListener> weakEventListener = new WeakReference<EventListener>(eventListener);
+        list.add(weakEventListener);
+      }
+    }
+  }
+
+  @Override
+  public void unregisterListener(Event event, EventListener eventListener) {
+    List<WeakReference<EventListener>> list = null;
+    synchronized (events) {
+      if (events.containsKey(event.getName())) {
+        list = events.get(event.getName());
+      }
+    }
+    if (list != null) {
+      synchronized (list) {
+        for (Iterator<WeakReference<EventListener>> iter = list.iterator(); iter.hasNext();) {
+          WeakReference<EventListener> listener = iter.next();
+          if (listener.isEnqueued()) {
+            iter.remove();
+          } else {
+            if (eventListener.equals(listener.get())) {
+              iter.remove();
+            }
+          }
+        }
+      }
+    }
+  }
+
+  @Override
+  public void fireEvent(Event event, Object... data) {
+    List<WeakReference<EventListener>> list = null;
+    synchronized (events) {
+      if (events.containsKey(event.getName())) {
+        list = events.get(event.getName());
+      }
+    }
+    if (list != null) {
+      synchronized (list) {
+        for (Iterator<WeakReference<EventListener>> iter = list.iterator(); iter.hasNext();) {
+          WeakReference<EventListener> weakListener = iter.next();
+          if (weakListener.isEnqueued()) {
+            iter.remove();
+          } else if (weakListener.get() == null) {
+            iter.remove();
+          } else {
+            weakListener.get().handle(event.getName(), data);
+          }
+        }
+      }
+    }
+  }
+
+  @Override
+  public boolean isEventRegistered(Event event1) {
+    return events.containsKey(event1.getName());
+  }
+
+  @Override
+  public boolean isListenerRegistered(Event event, EventListener eventListener) {
+    List<WeakReference<EventListener>> list = null;
+    synchronized (events) {
+      if (events.containsKey(event.getName())) {
+        list = events.get(event.getName());
+      }
+    }
+    if (list != null) {
+      synchronized (list) {
+        for (Iterator<WeakReference<EventListener>> iter = list.iterator(); iter.hasNext();) {
+          WeakReference<EventListener> listener = iter.next();
+          if (listener.isEnqueued()) {
+            iter.remove();
+          } else {
+            if (eventListener.equals(listener.get())) {
+              return true;
+            }
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public void removeAllListeners(Event event) {
+    List<WeakReference<EventListener>> list = null;
+    synchronized (events) {
+      if (events.containsKey(event.getName())) {
+        list = events.get(event.getName());
+      }
+    }
+    if (list != null) {
+      synchronized (list) {
+        list.clear();
+      }
+    }
+  }
+}

+ 20 - 0
src/main/java/de/mcs/utils/event/Event.java

@@ -0,0 +1,20 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2012 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: Event.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 12.03.2012 Willie
+ */
+
+package de.mcs.utils.event;
+
+/**
+ * @author Willie
+ *
+ */
+public interface Event {
+
+  public String getName();
+
+}

+ 189 - 0
src/main/java/de/mcs/utils/event/EventFactory.java

@@ -0,0 +1,189 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2012 by Wilfried Klaas
+ * Project: SmallArchivePivotGUI
+ * File: EventFactory.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 11.03.2012 Willie
+ */
+
+package de.mcs.utils.event;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Willie
+ *
+ */
+public class EventFactory {
+
+  private static EventFactory instance = new EventFactory();
+  private Map<String, List<WeakReference<EventListener>>> events;;
+  private List<WeakReference<EventListener>> generalListeners;
+
+  public EventFactory() {
+    this.events = new HashMap<String, List<WeakReference<EventListener>>>();
+    this.generalListeners = new ArrayList<WeakReference<EventListener>>();
+  }
+
+  public void registerEvent(Event event) {
+    synchronized (events) {
+      if (!events.containsKey(event.getName())) {
+        events.put(event.getName(), new ArrayList<WeakReference<EventListener>>());
+      }
+    }
+  }
+
+  public void registerListener(Event event, EventListener eventListener) {
+    List<WeakReference<EventListener>> list = null;
+    synchronized (events) {
+      if (events.containsKey(event.getName())) {
+        list = events.get(event.getName());
+      }
+    }
+    if (list != null) {
+      synchronized (list) {
+        for (Iterator<WeakReference<EventListener>> iter = list.iterator(); iter.hasNext();) {
+          WeakReference<EventListener> listener = iter.next();
+          if (listener.isEnqueued()) {
+            iter.remove();
+          } else {
+            if (eventListener.equals(listener.get())) {
+              return;
+            }
+          }
+        }
+        WeakReference<EventListener> weakEventListener = new WeakReference<EventListener>(eventListener);
+        list.add(weakEventListener);
+      }
+    }
+  }
+
+  public void unregisterListener(Event event, EventListener eventListener) {
+    List<WeakReference<EventListener>> list = null;
+    synchronized (events) {
+      if (events.containsKey(event.getName())) {
+        list = events.get(event.getName());
+      }
+    }
+    if (list != null) {
+      synchronized (list) {
+        for (Iterator<WeakReference<EventListener>> iter = list.iterator(); iter.hasNext();) {
+          WeakReference<EventListener> listener = iter.next();
+          if (listener.isEnqueued()) {
+            iter.remove();
+          } else {
+            if (eventListener.equals(listener.get())) {
+              iter.remove();
+            }
+          }
+        }
+      }
+    }
+  }
+
+  public void fireEvent(Event event, Object... data) {
+    List<WeakReference<EventListener>> list = null;
+    synchronized (events) {
+      if (events.containsKey(event.getName())) {
+        list = events.get(event.getName());
+      }
+    }
+    if (list != null) {
+      synchronized (list) {
+        for (Iterator<WeakReference<EventListener>> iter = list.iterator(); iter.hasNext();) {
+          WeakReference<EventListener> weakListener = iter.next();
+          if (weakListener.isEnqueued()) {
+            iter.remove();
+          } else if (weakListener.get() == null) {
+            iter.remove();
+          } else {
+            weakListener.get().handle(event.getName(), data);
+          }
+        }
+      }
+    }
+    synchronized (generalListeners) {
+      for (Iterator<WeakReference<EventListener>> iter = generalListeners.iterator(); iter.hasNext();) {
+        WeakReference<EventListener> weakListener = iter.next();
+        if (weakListener.isEnqueued()) {
+          iter.remove();
+        } else if (weakListener.get() == null) {
+          iter.remove();
+        } else {
+          weakListener.get().handle(event.getName(), data);
+        }
+      }
+    }
+  }
+
+  public boolean isEventRegistered(Event event1) {
+    return events.containsKey(event1.getName());
+  }
+
+  public boolean isListenerRegistered(Event event, EventListener eventListener) {
+    List<WeakReference<EventListener>> list = null;
+    synchronized (events) {
+      if (events.containsKey(event.getName())) {
+        list = events.get(event.getName());
+      }
+    }
+    if (list != null) {
+      synchronized (list) {
+        for (Iterator<WeakReference<EventListener>> iter = list.iterator(); iter.hasNext();) {
+          WeakReference<EventListener> listener = iter.next();
+          if (listener.isEnqueued()) {
+            iter.remove();
+          } else {
+            if (eventListener.equals(listener.get())) {
+              return true;
+            }
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  public static EventFactory getInstance() {
+    return instance;
+  }
+
+  public void registerGeneralListener(EventListener eventListener) {
+    synchronized (generalListeners) {
+      for (Iterator<WeakReference<EventListener>> iter = generalListeners.iterator(); iter.hasNext();) {
+        WeakReference<EventListener> listener = iter.next();
+        if (listener.isEnqueued()) {
+          iter.remove();
+        } else {
+          if (eventListener.equals(listener.get())) {
+            return;
+          }
+        }
+      }
+      WeakReference<EventListener> weakEventListener = new WeakReference<EventListener>(eventListener);
+      generalListeners.add(weakEventListener);
+    }
+  }
+
+  public void unregisterGeneralListener(EventListener eventListener) {
+    synchronized (generalListeners) {
+      for (Iterator<WeakReference<EventListener>> iter = generalListeners.iterator(); iter.hasNext();) {
+        WeakReference<EventListener> listener = iter.next();
+        if (listener.isEnqueued()) {
+          iter.remove();
+        } else {
+          if (eventListener.equals(listener.get())) {
+            iter.remove();
+          }
+        }
+      }
+    }
+  }
+
+}

+ 20 - 0
src/main/java/de/mcs/utils/event/EventListener.java

@@ -0,0 +1,20 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2012 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: EventListener.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 12.03.2012 Willie
+ */
+
+package de.mcs.utils.event;
+
+/**
+ * @author Willie
+ *
+ */
+public interface EventListener {
+
+  void handle(String eventName, Object... data);
+
+}

+ 103 - 0
src/main/java/de/mcs/utils/event/ManagedProperty.java

@@ -0,0 +1,103 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2012 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: ManagedProperty.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 14.03.2012 Willie
+ */
+
+package de.mcs.utils.event;
+
+import java.util.List;
+
+import de.mcs.utils.event.CallbackList.ListCallback;
+import de.mcs.utils.event.CallbackList.Operation;
+
+/**
+ * @author Willie
+ *
+ */
+public class ManagedProperty<E> implements ListCallback {
+
+  public class ManagedEvent extends AbstractEvent {
+
+    ManagedEvent(String name) {
+      super(name);
+    }
+
+  }
+
+  private E value;
+  private Delegate delegate;
+  private ManagedEvent event;
+  private boolean fireEventActive;
+
+  public ManagedProperty(String name, Delegate delegate, E initialValue) {
+    this.value = initialValue;
+    this.delegate = delegate;
+    this.fireEventActive = true;
+    this.event = new ManagedEvent(name);
+    delegate.registerEvent(event);
+    if (value instanceof List) {
+      List<E> initialList = (List<E>) value;
+      CallbackList<E> list = new CallbackList<E>(initialList);
+      list.addCallback(this);
+      value = (E) list;
+    }
+  }
+
+  public void setValue(E newValue) {
+    setValue(newValue, true);
+  }
+
+  public void setValue(E newValue, boolean fireEvent) {
+    if (value != null) {
+      if (!value.equals(newValue)) {
+        E oldValue = value;
+        value = newValue;
+        if (fireEvent) {
+          delegate.fireEvent(event, new Object[] { oldValue, newValue });
+        }
+      }
+    } else if (newValue != null) {
+      value = newValue;
+      if (fireEvent) {
+        delegate.fireEvent(event, new Object[] { value, newValue });
+      }
+    }
+  }
+
+  public void fireEvent() {
+    if (fireEventActive) {
+      delegate.fireEvent(event, new Object[] { value, value });
+    }
+  }
+
+  public E getValue() {
+    return value;
+  }
+
+  public void registerListener(EventListener listener) {
+    delegate.registerListener(event, listener);
+  }
+
+  public void unregisterListener(EventListener listener) {
+    delegate.unregisterListener(event, listener);
+  }
+
+  @Override
+  public void handle(Operation operation) {
+    if (fireEventActive) {
+      delegate.fireEvent(event, new Object[] { operation.name() });
+    }
+  }
+
+  public void setFireEvents(boolean value) {
+    this.fireEventActive = value;
+  }
+
+  public boolean isFireEvents() {
+    return this.fireEventActive;
+  }
+}

+ 194 - 0
src/main/java/de/mcs/utils/io/RandomAccessInputStream.java

@@ -0,0 +1,194 @@
+/* RandomAccessInputStream
+ *
+ * Created on May 21, 2004
+ *
+ * Copyright (C) 2004 Internet Archive.
+ *
+ * This file is part of the Heritrix web crawler (crawler.archive.org).
+ *
+ * Heritrix is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * any later version.
+ *
+ * Heritrix is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with Heritrix; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package de.mcs.utils.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+
+/**
+ * Wraps a RandomAccessFile with an InputStream interface.
+ * 
+ * @author gojomo
+ */
+public class RandomAccessInputStream extends InputStream {
+
+  /**
+   * Reference to the random access file this stream is reading from.
+   */
+  private RandomAccessFile raf = null;
+
+  /**
+   * When mark is called, save here the current position so we can go back on
+   * reset.
+   */
+  private long markpos = -1;
+
+  /**
+   * True if we are to close the underlying random access file when this stream
+   * is closed.
+   */
+  private boolean sympathyClose;
+
+  /**
+   * Constructor.
+   * 
+   * If using this constructor, caller created the RAF and therefore its assumed
+   * wants to control close of the RAF. The RAF.close is not called if this
+   * constructor is used on close of this stream.
+   * 
+   * @param raf
+   *          RandomAccessFile to wrap.
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public RandomAccessInputStream(RandomAccessFile raf) throws IOException {
+    this(raf, false, 0);
+  }
+
+  /**
+   * Constructor.
+   * 
+   * @param file
+   *          File to get RAFIS on. Creates an RAF from passed file. Closes the
+   *          created RAF when this stream is closed.
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public RandomAccessInputStream(final File file) throws IOException {
+    this(new RandomAccessFile(file, "r"), true, 0);
+  }
+
+  /**
+   * Constructor.
+   * 
+   * @param file
+   *          File to get RAFIS on. Creates an RAF from passed file. Closes the
+   *          created RAF when this stream is closed.
+   * @param offset
+   *          offset to begin with
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public RandomAccessInputStream(final File file, final long offset) throws IOException {
+    this(new RandomAccessFile(file, "r"), true, offset);
+  }
+
+  /**
+   * @param raf
+   *          RandomAccessFile to wrap.
+   * @param sympathyClose
+   *          Set to true if we are to close the RAF file when this stream is
+   *          closed.
+   * @param offset
+   *          offset to start with
+   * @throws IOException
+   *           if something goes wrong
+   */
+  public RandomAccessInputStream(final RandomAccessFile raf, final boolean sympathyClose, final long offset)
+      throws IOException {
+    super();
+    this.sympathyClose = sympathyClose;
+    this.raf = raf;
+    if (offset > 0) {
+      this.raf.seek(offset);
+    }
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.InputStream#read()
+   */
+  public int read() throws IOException {
+    return this.raf.read();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.InputStream#read(byte[], int, int)
+   */
+  public int read(byte[] b, int off, int len) throws IOException {
+    return this.raf.read(b, off, len);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.InputStream#read(byte[])
+   */
+  public int read(byte[] b) throws IOException {
+    return this.raf.read(b);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.InputStream#skip(long)
+   */
+  public long skip(long n) throws IOException {
+    this.raf.seek(this.raf.getFilePointer() + n);
+    return n;
+  }
+
+  public long position() throws IOException {
+    return this.raf.getFilePointer();
+  }
+
+  public void position(long position) throws IOException {
+    this.raf.seek(position);
+  }
+
+  public int available() throws IOException {
+    long amount = this.raf.length() - this.position();
+    return (amount >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) amount;
+  }
+
+  public boolean markSupported() {
+    return true;
+  }
+
+  public synchronized void mark(int readlimit) {
+    try {
+      this.markpos = position();
+    } catch (IOException e) {
+      // Set markpos to -1. Will cause exception reset.
+      this.markpos = -1;
+    }
+  }
+
+  public synchronized void reset() throws IOException {
+    if (this.markpos == -1) {
+      throw new IOException("Mark has not been set.");
+    }
+    position(this.markpos);
+  }
+
+  public void close() throws IOException {
+    if (this.sympathyClose) {
+      this.raf.close();
+    }
+  }
+}

+ 84 - 0
src/main/java/de/mcs/utils/io/RandomAccessOutputStream.java

@@ -0,0 +1,84 @@
+/* Copyright (C) 2003 Internet Archive.
+ *
+ * This file is part of the Heritrix web crawler (crawler.archive.org).
+ *
+ * Heritrix is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * any later version.
+ *
+ * Heritrix is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with Heritrix; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * RandomAccessOutputStream.java
+ * Created on May 21, 2004
+ *
+ * $Header$
+ */
+package de.mcs.utils.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+
+/**
+ * Wraps a RandomAccessFile with OutputStream interface.
+ *
+ * @author gojomo
+ */
+public class RandomAccessOutputStream extends OutputStream {
+  RandomAccessFile raf;
+
+  /**
+   * Wrap the given RandomAccessFile
+   * 
+   * @param raf
+   *          the file
+   */
+  public RandomAccessOutputStream(RandomAccessFile raf) {
+    super();
+    this.raf = raf;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.OutputStream#write(int)
+   */
+  public void write(int b) throws IOException {
+    raf.write(b);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.OutputStream#close()
+   */
+  public void close() throws IOException {
+    raf.close();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.OutputStream#write(byte[], int, int)
+   */
+  public void write(byte[] b, int off, int len) throws IOException {
+    raf.write(b, off, len);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.OutputStream#write(byte[])
+   */
+  public void write(byte[] b) throws IOException {
+    raf.write(b);
+  }
+}

+ 72 - 0
src/main/java/de/mcs/utils/jsap/CommandKey.java

@@ -0,0 +1,72 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2009 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: CommandKey.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 20.11.2009 Willie
+ */
+
+package de.mcs.utils.jsap;
+
+/**
+ * @author Willie
+ */
+public class CommandKey {
+  private Character shortKey;
+
+  private String longKey;
+
+  private String name;
+
+  private String help;
+
+  private String defaultValue;
+
+  private boolean required;
+
+  public CommandKey(char shortKey, String longKey, String name, String help, String defaultValue) {
+    this(shortKey, longKey, name, help);
+    this.defaultValue = defaultValue;
+  }
+
+  public CommandKey(char shortKey, String longKey, String name, String help, boolean required) {
+    this(shortKey, longKey, name, help);
+    this.required = required;
+  }
+
+  public CommandKey(char shortKey, String longKey, String name, String help) {
+    this.shortKey = shortKey;
+    this.longKey = longKey;
+    this.name = name;
+    this.help = help;
+    this.required = false;
+  }
+
+  public Character getShortKey() {
+    return shortKey;
+  }
+
+  public String getLongKey() {
+    return longKey;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getHelp() {
+    return help;
+  }
+
+  /**
+   * @return the defaultValue
+   */
+  public String getDefaultValue() {
+    return defaultValue;
+  }
+
+  public boolean isRequired() {
+    return required;
+  }
+}

+ 55 - 0
src/main/java/de/mcs/utils/jsap/FileOption.java

@@ -0,0 +1,55 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: Option.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 29.11.2018 wklaa_000
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+package de.mcs.utils.jsap;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target({ FIELD, METHOD })
+/**
+ * @author wklaa_000
+ *
+ */
+public @interface FileOption {
+  char shortKey() default ' ';
+
+  String longKey() default "";
+
+  String name();
+
+  String help();
+
+  String defaultValue() default "";
+
+  boolean required() default false;
+
+  boolean mustBeDirectory() default false;
+
+  boolean mustExists() default false;
+
+  int index() default 0;
+}

+ 47 - 0
src/main/java/de/mcs/utils/jsap/JSAPHelper.java

@@ -0,0 +1,47 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2009 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: JSAPHelper.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 20.11.2009 Willie
+ */
+
+package de.mcs.utils.jsap;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPException;
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.Switch;
+
+/**
+ * @author Willie
+ */
+public class JSAPHelper {
+
+  public static void registerAsSwitch(JSAP parser, CommandKey command) throws JSAPException {
+    Switch swtOption = new Switch(command.getName(), command.getShortKey(), command.getLongKey());
+    if (command.getDefaultValue() != null) {
+      swtOption.setDefault(command.getDefaultValue());
+    }
+    swtOption.setHelp(command.getHelp());
+    parser.registerParameter(swtOption);
+  }
+
+  public static void registerAsOption(JSAP parser, CommandKey command, StringParser stringParser, String defaultValue,
+      boolean required) throws JSAPException {
+    FlaggedOption flgopt = new FlaggedOption(command.getName(), stringParser, defaultValue, required,
+        command.getShortKey(), command.getLongKey());
+    flgopt.setHelp(command.getHelp());
+    parser.registerParameter(flgopt);
+  }
+
+  public static void registerAsOption(JSAP parser, CommandKey command, StringParser stringParser, boolean required)
+      throws JSAPException {
+    FlaggedOption flgopt = new FlaggedOption(command.getName(), stringParser, command.getDefaultValue(), required,
+        command.getShortKey(), command.getLongKey());
+    flgopt.setHelp(command.getHelp());
+    parser.registerParameter(flgopt);
+  }
+}

+ 253 - 0
src/main/java/de/mcs/utils/jsap/ProcessCommandline.java

@@ -0,0 +1,253 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: ProcessCommandline.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 29.11.2018 wklaa_000
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+package de.mcs.utils.jsap;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.reflections.Reflections;
+import org.reflections.scanners.FieldAnnotationsScanner;
+import org.reflections.scanners.MethodAnnotationsScanner;
+import org.reflections.util.FilterBuilder;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPException;
+import com.martiansoftware.jsap.JSAPResult;
+import com.martiansoftware.jsap.UnflaggedOption;
+import com.martiansoftware.jsap.stringparsers.FileStringParser;
+import com.martiansoftware.jsap.stringparsers.StringStringParser;
+
+/**
+ * @author wklaa_000
+ *
+ */
+public class ProcessCommandline {
+
+  public static final FilterBuilder TestModelFilter = new FilterBuilder().include("de.mcs.*\\$.*");
+
+  private static JSAP parser;
+  private static JSAPResult commandLineArgs;
+
+  private static Map<String, Method> parameterMethods;
+
+  public static void processCommandline(String[] args) {
+    parameterMethods = new HashMap<>();
+    parseParameters(args);
+    checkParams();
+    processParameters();
+  };
+
+  private static void processParameters() {
+    parameterMethods.entrySet().forEach(e -> {
+      String name = e.getKey();
+      Method method = e.getValue();
+      processSwitchOption(name, method);
+      processStringOption(name, method);
+      processFileOption(name, method);
+
+    });
+  }
+
+  private static void processFileOption(String name, Method method) {
+    FileOption annotation = method.getAnnotation(FileOption.class);
+    if (annotation != null) {
+      try {
+        method.invoke(null, commandLineArgs.getFile(annotation.name()));
+      } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
+        e1.printStackTrace();
+      }
+    }
+  }
+
+  private static void processStringOption(String name, Method method) {
+    StringOption annotation = method.getAnnotation(StringOption.class);
+    if (annotation != null) {
+      try {
+        method.invoke(null, commandLineArgs.getString(annotation.name()));
+      } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
+        e1.printStackTrace();
+      }
+    }
+  }
+
+  private static void processSwitchOption(String name, Method method) {
+    Switch annotation = method.getAnnotation(Switch.class);
+    if (annotation != null) {
+      try {
+        method.invoke(null, commandLineArgs.getBoolean(name));
+      } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
+        e1.printStackTrace();
+      }
+    }
+  }
+
+  /**
+   * checking commandline parameters.
+   */
+  private static void checkParams() {
+    if (!commandLineArgs.success()) {
+
+      System.err.println();
+
+      // print out specific error messages describing the problems
+      // with the command line, THEN print usage, THEN print full
+      // help. This is called "beating the user with a clue stick."
+      for (@SuppressWarnings("rawtypes")
+      Iterator errs = commandLineArgs.getErrorMessageIterator(); errs.hasNext();) {
+        System.err.println("Error: " + errs.next());
+      }
+      showHelp("error");
+      System.exit(-1);
+    }
+
+  }
+
+  /**
+   * showing the actual help page in console.
+   */
+  public static void showHelp(String message) {
+    System.out.println(message);
+    System.out.println(parser.getHelp());
+  }
+
+  /**
+   * This function parses the commandline parameters. If the parameter -h or
+   * --help is avalible, generate a little help text.
+   * 
+   * @param args
+   *          Commadline arguments
+   */
+  public static void parseParameters(final String[] args) {
+    parser = new JSAP();
+    try {
+      // registering the parameters with default values
+      registerDefaultParameter();
+      // parsing the commadline
+      commandLineArgs = parser.parse(args);
+    } catch (JSAPException e) {
+      // Something goes wrong
+      System.out.println("Parsing exception:" + e.getMessage());
+      e.printStackTrace();
+      System.exit(1);
+    }
+  }
+
+  /**
+   * registering the parameters to the JSAP Parser.
+   * 
+   * @throws JSAPException
+   *           Something is going wrong
+   */
+  private static void registerDefaultParameter() throws JSAPException {
+    Map<Integer, UnflaggedOption> unflaggedOptions = new HashMap<>();
+    Reflections reflections = new Reflections("", new MethodAnnotationsScanner(), new FieldAnnotationsScanner());
+
+    // JSAPHelper.registerAsSwitch(parser, HELP);
+
+    // MethodAnnotationsScanner
+    Set<Method> methodes = reflections.getMethodsAnnotatedWith(Switch.class);
+    for (Method method : methodes) {
+      Switch annotation = method.getAnnotation(Switch.class);
+      System.out.printf("short: %s, long: %s, name: %s, help: %s, def: %s, req: %s\r\n", annotation.shortKey(),
+          annotation.longKey(), annotation.name(), annotation.help(), annotation.defaultValue(), annotation.required());
+
+      com.martiansoftware.jsap.Switch swtOption = new com.martiansoftware.jsap.Switch(annotation.name(),
+          annotation.shortKey(), annotation.longKey());
+      if (annotation.defaultValue()) {
+        swtOption.setDefault(Boolean.TRUE.toString());
+      }
+      swtOption.setHelp(annotation.help());
+      parser.registerParameter(swtOption);
+      parameterMethods.put(annotation.name(), method);
+    }
+    // // FieldAnnotationsScanner
+    // Set<Field> fields = reflections.getFieldsAnnotatedWith(Switch.class);
+    // for (Field field : fields) {
+    // System.out.println(field.getName());
+    // }
+
+    // MethodAnnotationsScanner
+    methodes = reflections.getMethodsAnnotatedWith(StringOption.class);
+    for (Method method : methodes) {
+      StringOption annotation = method.getAnnotation(StringOption.class);
+      System.out.printf("short: %s, long: %s, name: %s, help: %s, def: %s, req: %s\r\n", annotation.shortKey(),
+          annotation.longKey(), annotation.name(), annotation.help(), annotation.defaultValue(), annotation.required());
+
+      StringStringParser stringParser = StringStringParser.getParser();
+      if (annotation.index() > 0) {
+        UnflaggedOption unflgopt = new UnflaggedOption(annotation.name(), stringParser, annotation.defaultValue(),
+            annotation.required(), false, annotation.help());
+        unflaggedOptions.put(annotation.index(), unflgopt);
+        parameterMethods.put(Integer.toString(annotation.index()), method);
+      } else {
+        FlaggedOption flgopt = new FlaggedOption(annotation.name(), stringParser, annotation.defaultValue(),
+            annotation.required(), annotation.shortKey(), annotation.longKey());
+        flgopt.setHelp(annotation.help());
+        parser.registerParameter(flgopt);
+        parameterMethods.put(annotation.name(), method);
+      }
+    }
+
+    // MethodAnnotationsScanner
+    methodes = reflections.getMethodsAnnotatedWith(FileOption.class);
+    for (Method method : methodes) {
+      FileOption annotation = method.getAnnotation(FileOption.class);
+      System.out.printf("short: %s, long: %s, name: %s, help: %s, def: %s, req: %s\r\n", annotation.shortKey(),
+          annotation.longKey(), annotation.name(), annotation.help(), annotation.defaultValue(), annotation.required());
+
+      FileStringParser fileStringParser = FileStringParser.getParser();
+      fileStringParser.setMustBeDirectory(annotation.mustBeDirectory());
+      fileStringParser.setMustExist(annotation.mustExists());
+
+      if (annotation.index() > 0) {
+        UnflaggedOption unflgopt = new UnflaggedOption(annotation.name(), fileStringParser, annotation.defaultValue(),
+            annotation.required(), false, annotation.help());
+        unflaggedOptions.put(annotation.index(), unflgopt);
+        parameterMethods.put(Integer.toString(annotation.index()), method);
+      } else {
+        FlaggedOption flgopt = new FlaggedOption(annotation.name(), fileStringParser, annotation.defaultValue(),
+            annotation.required(), annotation.shortKey(), annotation.longKey());
+        flgopt.setHelp(annotation.help());
+        parser.registerParameter(flgopt);
+        parameterMethods.put(annotation.name(), method);
+      }
+    }
+
+    List<Integer> indexes = new ArrayList<>();
+    unflaggedOptions.keySet().forEach(e -> indexes.add(e));
+    indexes.sort((x, y) -> {
+      int diff = x - y;
+      return (diff == 0) ? 1 : diff;
+    });
+
+    for (Integer e : indexes) {
+      parser.registerParameter(unflaggedOptions.get(e));
+    }
+  }
+}

+ 51 - 0
src/main/java/de/mcs/utils/jsap/StringOption.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: Option.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 29.11.2018 wklaa_000
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+package de.mcs.utils.jsap;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target({ FIELD, METHOD })
+/**
+ * @author wklaa_000
+ *
+ */
+public @interface StringOption {
+  char shortKey() default ' ';
+
+  String longKey() default "";
+
+  String name();
+
+  String help();
+
+  String defaultValue() default "";
+
+  boolean required() default false;
+
+  int index() default 0;
+}

+ 49 - 0
src/main/java/de/mcs/utils/jsap/Switch.java

@@ -0,0 +1,49 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: Switch.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 29.11.2018 wklaa_000
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+package de.mcs.utils.jsap;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target({ FIELD, METHOD })
+/**
+ * @author wklaa_000
+ *
+ */
+public @interface Switch {
+  char shortKey();
+
+  String longKey();
+
+  String name();
+
+  String help();
+
+  boolean defaultValue();
+
+  boolean required();
+}

+ 16 - 0
src/main/java/de/mcs/utils/nio/WatchCallBack.java

@@ -0,0 +1,16 @@
+/**
+ * 
+ */
+package de.mcs.utils.nio;
+
+import java.nio.file.Path;
+
+/**
+ * @author w.klaas
+ *
+ */
+public interface WatchCallBack {
+
+  void modified(Path path);
+
+}

+ 267 - 0
src/main/java/de/mcs/utils/nio/WatchDir.java

@@ -0,0 +1,267 @@
+/**
+ * 
+ */
+package de.mcs.utils.nio;
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   - Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   - Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ *   - Neither the name of Oracle nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
+
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchEvent.Kind;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Example to watch a directory (or tree) for changes to files.
+ */
+
+public class WatchDir {
+
+  private WatchService watcher;
+  private final Map<WatchKey, Path> keys;
+  private final boolean recursive;
+  private boolean trace = false;
+  private Thread fileSystemPoller;
+
+  @SuppressWarnings("unchecked")
+  static <T> WatchEvent<T> cast(WatchEvent<?> event) {
+    return (WatchEvent<T>) event;
+  }
+
+  /**
+   * Register the given directory with the WatchService
+   */
+  private void register(Path dir) throws IOException {
+    WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
+    if (trace) {
+      Path prev = keys.get(key);
+      if (prev == null) {
+      } else {
+        if (!dir.equals(prev)) {
+        }
+      }
+    }
+    keys.put(key, dir);
+  }
+
+  /**
+   * Register the given directory, and all its sub-directories, with the
+   * WatchService.
+   */
+  private void registerAll(final Path start) throws IOException {
+    // register directory and sub-directories
+    Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
+      @Override
+      public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+        if (dir.toFile().getName().startsWith(".")) {
+          return FileVisitResult.SKIP_SUBTREE;
+        }
+        register(dir);
+        return FileVisitResult.CONTINUE;
+      }
+    });
+  }
+
+  /**
+   * Creates a WatchService and registers the given directory
+   * 
+   * @param dir
+   *          directory to register
+   * @param recursive
+   *          register sub folder
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public WatchDir(Path dir, boolean recursive) throws IOException {
+    this.watcher = FileSystems.getDefault().newWatchService();
+    this.keys = new HashMap<WatchKey, Path>();
+    this.recursive = recursive;
+
+    if (recursive) {
+      registerAll(dir);
+    } else {
+      register(dir);
+    }
+
+    // enable trace after initial registration
+    this.trace = true;
+  }
+
+  public void startWatchThread(final WatchCallBack callback) throws InterruptedException {
+    if (fileSystemPoller != null) {
+      if (fileSystemPoller.isAlive()) {
+        fileSystemPoller.interrupt();
+        fileSystemPoller.join();
+      }
+    }
+    fileSystemPoller = new Thread(new Runnable() {
+
+      @Override
+      public void run() {
+        while (!fileSystemPoller.isInterrupted()) {
+          processEvents(callback);
+        }
+      }
+    }, "filesystempoller");
+    fileSystemPoller.setDaemon(true);
+    fileSystemPoller.start();
+  }
+
+  public void stopWatchThread() throws InterruptedException {
+    if (fileSystemPoller != null) {
+      if (fileSystemPoller.isAlive()) {
+        fileSystemPoller.interrupt();
+        fileSystemPoller.join();
+      }
+      fileSystemPoller = null;
+    }
+  }
+
+  public boolean isActive() {
+    return (fileSystemPoller != null) && fileSystemPoller.isAlive();
+  }
+
+  /**
+   * Process all events for keys queued to the watcher
+   * 
+   * @param callback
+   *          the callback method to call
+   */
+  public void processEvents(WatchCallBack callback) {
+    for (;;) {
+
+      // wait for key to be signalled
+      WatchKey key;
+      // try {
+      key = watcher.poll();
+      // } catch (InterruptedException x) {
+      // return;
+      // }
+
+      if (key == null) {
+        return;
+      }
+      Path dir = keys.get(key);
+      if (dir == null) {
+        continue;
+      }
+
+      for (WatchEvent<?> event : key.pollEvents()) {
+        Kind<?> kind = event.kind();
+
+        // TBD - provide example of how OVERFLOW event is handled
+        if (kind == OVERFLOW) {
+          continue;
+        }
+
+        // Context for directory entry event is the file name of entry
+        WatchEvent<Path> ev = cast(event);
+        Path name = ev.context();
+        Path child = dir.resolve(name);
+
+        if (callback != null) {
+          callback.modified(child);
+        }
+        // print out event
+        // System.out.format("%s: %s\n", event.kind().name(), child);
+
+        // if directory is created, and watching recursively, then
+        // register it and its sub-directories
+        if (recursive && (kind == ENTRY_CREATE)) {
+          try {
+            if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
+              registerAll(child);
+            }
+          } catch (IOException x) {
+            // ignore to keep sample readbale
+          }
+        }
+      }
+
+      // reset key and remove from set if directory no longer accessible
+      boolean valid = key.reset();
+      if (!valid) {
+        keys.remove(key);
+
+        // all directories are inaccessible
+        if (keys.isEmpty()) {
+          break;
+        }
+      }
+    }
+  }
+
+  static void usage() {
+    System.err.println("usage: java WatchDir [-r] dir");
+    System.exit(-1);
+  }
+
+  public static void main(String[] args) throws IOException {
+    // parse arguments
+    if (args.length == 0 || args.length > 2)
+      usage();
+    boolean recursive = false;
+    int dirArg = 0;
+    if (args[0].equals("-r")) {
+      if (args.length < 2)
+        usage();
+      recursive = true;
+      dirArg++;
+    }
+
+    // register directory and process its events
+    Path dir = Paths.get(args[dirArg]);
+    new WatchDir(dir, recursive).processEvents(null);
+  }
+
+  public void remove() throws IOException {
+    if (watcher != null) {
+      watcher.close();
+      watcher = null;
+    }
+  }
+}

+ 79 - 0
src/main/java/de/mcs/utils/nio/WatchDirThread.java

@@ -0,0 +1,79 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2012 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: WatchDirThread.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 23.02.2012 Willie
+ */
+
+package de.mcs.utils.nio;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+/**
+ * @author Willie
+ * 
+ */
+public class WatchDirThread extends Thread {
+
+  private Path watchDir;
+  private WatchDir watcher;
+  private WatchCallBack callback;
+  private boolean enable;
+  private boolean recursive;
+
+  public WatchDirThread(WatchCallBack callback, Path watchDir, boolean recursive) throws IOException {
+    this.watchDir = watchDir;
+    this.callback = callback;
+    this.recursive = recursive;
+    this.setEnable(true);
+    this.setDaemon(true);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.lang.Thread#run()
+   */
+  @Override
+  public void run() {
+    try {
+      watcher = new WatchDir(watchDir, recursive);
+      while (!isInterrupted()) {
+        if (isEnable()) {
+          watcher.processEvents(callback);
+        } else {
+          watcher.processEvents(new WatchCallBack() {
+            @Override
+            public void modified(Path path) {
+              // Do nothing here
+            }
+          });
+        }
+        Thread.yield();
+      }
+      watcher.remove();
+      watcher = null;
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * @return the enable
+   */
+  public boolean isEnable() {
+    return enable;
+  }
+
+  /**
+   * @param enable
+   *          the enable to set
+   */
+  public void setEnable(boolean enable) {
+    this.enable = enable;
+  }
+
+}

+ 258 - 0
src/main/java/de/mcs/utils/statistics/Count.java

@@ -0,0 +1,258 @@
+package de.mcs.utils.statistics;
+
+/*
+ * Count.java
+ * Project: LEAF
+ * WHEN WHO WHAT DESCRIPTION
+ * BRY Creation
+ * Copyright 2001 by ELCA Informatique SA
+ * Av. de la Harpe 22-24, 1000 Lausanne 13
+ * All rights reserved.
+ * This software is the confidential and proprietary information
+ * of ELCA Informatique SA. ("Confidential Information"). You
+ * shall not disclose such Confidential Information and shall
+ * use it only in accordance with the terms of the license
+ * agreement you entered into with ELCA.
+ */
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+
+class ActionCount {
+
+  int lines;
+
+  int realComments;
+
+  int realCode;
+
+  int commentsToCode;
+
+  String name;
+
+  private static void assertCond(boolean cond, String msg) {
+    if (!cond) {
+      System.out.println("assertCond FAILED: " + msg);
+      System.exit(1);
+    }
+    ;
+  };
+
+  static public String justify(int s) {
+    return justify(s, 6);
+  };
+
+  static public String justify(int s, int i) {
+    String b = "                                         ";
+    String n = s + "";
+    return b.substring(0, i - n.length()) + n;
+  };
+
+  public void prepare() {
+  };
+
+  public void done() {
+  };
+
+  public void exec(File dir, String filename) {
+    boolean proceed = false;
+    for (String type : Count.FILESTOCOUNT) {
+      if (filename.endsWith(type))
+        proceed = true;
+    }
+    if (!proceed || filename.equals("Count.java")) {
+      return;
+    }
+    ;
+    lines = 0;
+    realComments = 0;
+    realCode = 0;
+    File filein = new File(dir, filename);
+    name = filein.toString();
+    convert(filein.toString());
+    commentsToCode = (realCode == 0 ? 1000 : 100 * realComments / realCode);
+    System.out.println("Lines:" + justify(lines) + " Cmt=" + justify(realComments) + " Code=" + justify(realCode)
+        + " Ratio=" + justify(commentsToCode) + "% " + name);
+    Count.lines += lines;
+    Count.realComments += realComments;
+    Count.realCode += realCode;
+    Count.commentsToCode += commentsToCode;
+    Count.fileCount++;
+  };
+
+  private void countLine(String line) {
+    int pos;
+    // javadoc
+    if (line.indexOf("/**") > 0) {
+      if (line.trim().length() > 3) {
+        realComments++;
+      }
+      return;
+    }
+
+    if (line.indexOf("*") > 0) {
+      if (line.trim().length() > 3) {
+        realComments++;
+        // System.out.println("##"+line) ;
+        return;
+      }
+    }
+    ;
+
+    if ((line.trim().indexOf("/*") == 1) && (line.indexOf("*/") > 0)) {
+      realComments++;
+      // System.out.println("##"+line) ;
+      return;
+    }
+
+    if (line.indexOf("*/") > 0)
+      return;
+
+    pos = line.indexOf("//");
+    if (pos >= 0) {
+      String t = line.substring(pos + 2).trim();
+      if (t.length() > 0) {
+        realComments++;
+        // System.out.println("##"+line) ;
+        return;
+      }
+      ;
+    } else {
+      String t = line.trim();
+      if (t.length() > 3) {
+        realCode++;
+        // System.out.println("CC"+line) ;
+        return;
+      }
+    }
+    ;
+    // System.out.println(" "+line) ;
+  };
+
+  public void convert(String filename) {
+    FileInputStream inputStream = null;
+    String line = null;
+    try {
+      inputStream = new FileInputStream(filename);
+    } catch (Exception e) {
+      assertCond(false, "File not readable " + filename);
+    }
+    ;
+
+    BufferedReader file = new BufferedReader(new InputStreamReader(inputStream));
+
+    while (true) {
+      try {
+        line = file.readLine();
+        lines++;
+      } catch (Exception e) {
+        assertCond(false, "File not readable " + filename);
+      }
+      ;
+      if (line == null) {
+        break;
+      }
+      countLine(line);
+    }
+    ;
+
+    try {
+      file.close();
+    } catch (Exception e) {
+      assertCond(false, "File not closeable " + filename);
+    }
+    ;
+
+  }; // convert
+
+}; // end class ActionFormat
+
+public class Count {
+  public static int lines = 0;
+
+  public static int realComments = 0;
+
+  public static int realCode = 0;
+
+  public static int fileCount = 0;
+
+  public static int commentsToCode = 0;
+
+  private static final String[] DIRECTORIES = { "./src", "./test/src", "./webroot" };
+
+  protected static final String[] FILESTOCOUNT = { ".java", ".zul", ".vm" };
+
+  static public String justify(int s) {
+    return justify(s, 6);
+  };
+
+  static public String justify(int s, int i) {
+    String b = "                                         ";
+    String n = s + "";
+    return b.substring(0, i - n.length()) + n;
+  };
+
+  private static void assertCond(boolean cond, String msg) {
+    if (!cond) {
+      System.out.println("assertCond FAILED: " + msg);
+      System.exit(1);
+    }
+    ;
+  };
+
+  private static void reset() {
+    lines = 0;
+    realComments = 0;
+    realCode = 0;
+    fileCount = 0;
+    commentsToCode = 0;
+  }
+
+  public static void main(String args[]) {
+    ActionCount action = new ActionCount();
+    action.prepare();
+
+    for (String directory : DIRECTORIES) {
+      reset();
+      File d = new File(directory);
+      System.out.println("-----------------------------");
+      System.out.println("Counting Lines in all files " + d.getPath());
+      System.out.println("-----------------------------");
+      RecurseDir(action, d, 0);
+
+      action.done();
+      if (realCode > 0) {
+        commentsToCode = 100 * realComments / realCode;
+      }
+      System.out.println("-----------------------------");
+      System.out.println("Lines:" + justify(lines) + " Cmt=" + justify(realComments) + " Code=" + justify(realCode)
+          + " Ratio=" + justify(commentsToCode) + "% " + " Total files=" + fileCount);
+      System.out.println("-----------------------------");
+    }
+
+    action.prepare();
+
+  }
+
+  private static void RecurseDir(ActionCount action, java.io.File dir, int level) {
+    assertCond(level < 100, "no infinite recursion");
+    // File d = new File(dirName) ;
+    String[] list = dir.list();
+    int i;
+
+    if (list != null) { // some io sanity check
+
+      for (i = 0; i < list.length; i++) {
+        action.exec(dir, list[i]);
+        File f = new File(dir, list[i]);
+        assertCond(f != null, "f is null");
+        assertCond(list[i] != null, "list[i] is null");
+        if (f.isDirectory() && (!list[i].equals("Backup"))) {
+          RecurseDir(action, f, level + 1);
+        }
+      }
+    }
+  }
+
+}

+ 75 - 0
src/main/java/de/mcs/utils/threads/ThreadUtilities.java

@@ -0,0 +1,75 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2012 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: ThreadUtilities.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 30.03.2012 Willie
+ */
+
+package de.mcs.utils.threads;
+
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * @author Willie
+ *
+ */
+public class ThreadUtilities {
+
+  private static Thread consumerThread;
+  private static Queue<Runnable> queue = new ConcurrentLinkedQueue<Runnable>();
+
+  private static class ConsumerThread implements Runnable {
+    private Thread activeThread;
+
+    public void run() {
+      while (true) {
+        if ((activeThread == null) || (!activeThread.isAlive())) {
+          Runnable poll = queue.poll();
+          if (poll != null) {
+            activeThread = invoke(poll);
+          } else {
+            try {
+              Thread.sleep(100);
+            } catch (InterruptedException e) {
+              // e.printStackTrace();
+            }
+          }
+        }
+        // Thread.currentThread();
+        Thread.yield();
+      }
+    }
+  };
+
+  public static Thread invoke(Runnable runnable) {
+    Thread thread = new Thread(runnable);
+    thread.start();
+    return thread;
+  }
+
+  public static void invokeQueued(Runnable runnable) {
+    if (consumerThread == null) {
+      consumerThread = new Thread(new ConsumerThread());
+      consumerThread.setDaemon(true);
+      consumerThread.setName("background consumer thread");
+      consumerThread.start();
+    }
+    queue.offer(runnable);
+  }
+
+  public static boolean invokeAndWait(int maxTimeSec, Runnable runnable) {
+    Thread thread = invoke(runnable);
+    long stop = System.currentTimeMillis() + (maxTimeSec * 1000);
+    while (thread.isAlive() && (stop > System.currentTimeMillis())) {
+      Thread.yield();
+    }
+    if (thread.isAlive()) {
+      thread.interrupt();
+      return false;
+    }
+    return true;
+  }
+}

+ 9 - 0
src/main/resource/de/mcs/utils/codecs/package.html

@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+<body>
+MCSUtils - de.mcs.utils.codecs Some utility classes for encoding und
+decoding.
+</body>
+</html>

+ 8 - 0
src/main/resource/de/mcs/utils/package.html

@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+<body>
+MCSUtils - de.mcs.utils Some utility classes
+</body>
+</html>

+ 5 - 0
src/main/resource/overview.html

@@ -0,0 +1,5 @@
+<html>
+<body>
+MCSUtils is a free and simple java api for some things, you will ever use. See javadoc for more details. 
+</body>
+</html>

+ 2091 - 0
src/test/java/AllTests.java

@@ -0,0 +1,2091 @@
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+
+public class AllTests {
+  @SuppressWarnings("rawtypes")
+  public static void main(String[] args) throws Exception {
+    Class testClass = AllTests.class;
+    ArrayList<Method> setups = new ArrayList<Method>();
+    ArrayList<Method> tearDowns = new ArrayList<Method>();
+
+    for (Method method : testClass.getDeclaredMethods()) {
+      int modifiers = method.getModifiers();
+
+      if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && method.getAnnotation(Ignore.class) == null) {
+        if (method.getAnnotation(Before.class) != null) {
+          setups.add(method);
+        }
+
+        if (method.getAnnotation(After.class) != null) {
+          setups.add(method);
+        }
+      }
+    }
+
+    System.out.println("Starting all tests.");
+
+    Object instance = testClass.newInstance();
+
+    for (Method method : testClass.getDeclaredMethods()) {
+      int modifiers = method.getModifiers();
+
+      if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && method.getAnnotation(Ignore.class) == null) {
+        Test testAnnotation = method.getAnnotation(Test.class);
+
+        if (testAnnotation != null) {
+          for (Method setup : setups) {
+            setup.invoke(instance, (Object[]) null);
+          }
+
+          Class expectedException = testAnnotation.expected();
+
+          // can't for the life of me get eclipse to be able to
+          // resolve Test.None directly
+          if (expectedException.getName().equals("org.junit.Test$None")) // why
+                                                                         // the
+                                                                         // hell
+                                                                         // doesn't
+                                                                         // this
+          // just return null?
+          {
+            expectedException = null;
+          }
+
+          try {
+            method.invoke(instance, (Object[]) null);
+          } catch (Exception e) {
+            if (expectedException == null) {
+              System.out.println(testClass.getName() + "." + method.getName() + ": " + e.getCause().getMessage());
+              new BufferedReader(new InputStreamReader(System.in)).readLine();
+            } else {
+              // is there a cleaner way of saying this?
+              if (!e.getCause().getClass().equals(testAnnotation.expected())) {
+                System.out.println(testClass.getName() + "." + method.getName() + ": " + "Exception expected: "
+                    + testAnnotation.expected().getName() + ", Exception thrown: " + e.getCause().getMessage());
+                new BufferedReader(new InputStreamReader(System.in)).readLine();
+              }
+
+              expectedException = null;
+            }
+          }
+
+          if (expectedException != null) {
+            System.out.println(testClass.getName() + "." + method.getName() + ": " + "Expected exception not thrown: "
+                + testAnnotation.expected().getName());
+            new BufferedReader(new InputStreamReader(System.in)).readLine();
+          }
+
+          for (Method tearDown : tearDowns) {
+            tearDown.invoke(instance, (Object[]) null);
+          }
+        } // end if (testAnnotation != null)
+      } // end if (Modifier.isPublic(modifiers)...
+    } // end for (Method method : testClass.getDeclaredMethods())
+
+    System.out.println("Done with all tests.");
+  }
+
+  private static String generateString(char letter, int count) {
+    StringBuffer buffer = new StringBuffer(count);
+    for (int i = 0; i < count; i++) {
+      buffer.append(letter);
+    }
+    return buffer.toString();
+  }
+
+  private static void assertException(Exception expected, Exception actual) {
+    Assert.assertEquals(expected.getClass(), actual.getClass());
+    Assert.assertEquals(expected.getMessage(), actual.getMessage());
+  }
+
+  @Test
+  public void test1() throws Exception {
+    CsvReader reader = CsvReader.parse("1,2");
+    Assert.assertEquals("", reader.getRawRecord());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals("2", reader.get(1));
+    Assert.assertEquals(',', reader.getDelimiter());
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertEquals("1,2", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test2() throws Exception {
+    String data = "\"bob said, \"\"Hey!\"\"\",2, 3 ";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("bob said, \"Hey!\"", reader.get(0));
+    Assert.assertEquals("2", reader.get(1));
+    Assert.assertEquals("3", reader.get(2));
+    Assert.assertEquals(',', reader.getDelimiter());
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(3, reader.getColumnCount());
+    Assert.assertEquals("\"bob said, \"\"Hey!\"\"\",2, 3 ", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test3() throws Exception {
+    String data = ",";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals("", reader.get(1));
+    Assert.assertEquals(',', reader.getDelimiter());
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertEquals(",", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test4() throws Exception {
+    String data = "1\r2";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("2", reader.get(0));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("2", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test5() throws Exception {
+    String data = "1\n2";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("2", reader.get(0));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("2", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test6() throws Exception {
+    String data = "1\r\n2";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("2", reader.get(0));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("2", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test7() throws Exception {
+    String data = "1\r";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test8() throws Exception {
+    String data = "1\n";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test9() throws Exception {
+    String data = "1\r\n";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test10() throws Exception {
+    String data = "1\r2\n";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setDelimiter('\r');
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals("2", reader.get(1));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertEquals("1\r2", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test11() throws Exception {
+    String data = "\"July 4th, 2005\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("July 4th, 2005", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("\"July 4th, 2005\"", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test12() throws Exception {
+    String data = " 1";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setTrimWhitespace(false);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(" 1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals(" 1", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test13() throws Exception {
+    String data = "";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test14() throws Exception {
+    String data = "user_id,name\r\n1,Bruce";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readHeaders());
+    Assert.assertEquals("user_id,name", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals("Bruce", reader.get(1));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertEquals(0, reader.getIndex("user_id"));
+    Assert.assertEquals(1, reader.getIndex("name"));
+    Assert.assertEquals("user_id", reader.getHeader(0));
+    Assert.assertEquals("name", reader.getHeader(1));
+    Assert.assertEquals("1", reader.get("user_id"));
+    Assert.assertEquals("Bruce", reader.get("name"));
+    Assert.assertEquals("1,Bruce", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test15() throws Exception {
+    String data = "\"data \r\n here\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("data \r\n here", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("\"data \r\n here\"", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test16() throws Exception {
+    String data = "\r\r\n1\r";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setDelimiter('\r');
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals("", reader.get(1));
+    Assert.assertEquals("", reader.get(2));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(3, reader.getColumnCount());
+    Assert.assertEquals("\r\r", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals("", reader.get(1));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertEquals("1\r", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test17() throws Exception {
+    String data = "\"double\"\"\"\"double quotes\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("double\"\"double quotes", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("\"double\"\"\"\"double quotes\"", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test18() throws Exception {
+    String data = "1\r";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test19() throws Exception {
+    String data = "1\r\n";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test20() throws Exception {
+    String data = "1\n";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test21() throws Exception {
+    String data = "'bob said, ''Hey!''',2, 3 ";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setTextQualifier('\'');
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("bob said, 'Hey!'", reader.get(0));
+    Assert.assertEquals("2", reader.get(1));
+    Assert.assertEquals("3", reader.get(2));
+    Assert.assertEquals(',', reader.getDelimiter());
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(3, reader.getColumnCount());
+    Assert.assertEquals("'bob said, ''Hey!''',2, 3 ", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test22() throws Exception {
+    String data = "\"data \"\" here\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(false);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("\"data \"\" here\"", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("\"data \"\" here\"", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test23() throws Exception {
+    String data = generateString('a', 75) + "," + generateString('b', 75);
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(false);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(reader.get(0), generateString('a', 75));
+    Assert.assertEquals(reader.get(1), generateString('b', 75));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertEquals(generateString('a', 75) + "," + generateString('b', 75), reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test24() throws Exception {
+    String data = "1\r\n\r\n1";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(false);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test25() throws Exception {
+    String data = "1\r\n# bunch of crazy stuff here\r\n1";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(false);
+    reader.setUseComments(true);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test26() throws Exception {
+    String data = "\"Mac \"The Knife\" Peter\",\"Boswell, Jr.\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("Mac ", reader.get(0));
+    Assert.assertEquals("Boswell, Jr.", reader.get(1));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertEquals("\"Mac \"The Knife\" Peter\",\"Boswell, Jr.\"", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test27() throws Exception {
+    String data = "\"1\",Bruce\r\n\"2\n\",Toni\r\n\"3\",Brian\r\n";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals("Bruce", reader.get(1));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertEquals("\"1\",Bruce", reader.getRawRecord());
+    Assert.assertTrue(reader.skipRecord());
+    Assert.assertEquals("\"2\n\",Toni", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("3", reader.get(0));
+    Assert.assertEquals("Brian", reader.get(1));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertEquals("\"3\",Brian", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test28() throws Exception {
+    String data = "\"bob said, \\\"Hey!\\\"\",2, 3 ";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("bob said, \"Hey!\"", reader.get(0));
+    Assert.assertEquals("2", reader.get(1));
+    Assert.assertEquals("3", reader.get(2));
+    Assert.assertEquals(',', reader.getDelimiter());
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(3, reader.getColumnCount());
+    Assert.assertEquals("\"bob said, \\\"Hey!\\\"\",2, 3 ", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test29() throws Exception {
+    String data = "\"double\\\"\\\"double quotes\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("double\"\"double quotes", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("\"double\\\"\\\"double quotes\"", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test30() throws Exception {
+    String data = "\"double\\\\\\\\double backslash\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("double\\\\double backslash", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("\"double\\\\\\\\double backslash\"", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test31() throws Exception {
+    CsvWriter writer = new CsvWriter(new PrintWriter(new OutputStreamWriter(new FileOutputStream("temp.csv"),
+        Charset.forName("UTF-8"))), ',');
+    // writer will trim all whitespace and put this in quotes to preserve
+    // it's existance
+    writer.write(" \t \t");
+    writer.close();
+
+    CsvReader reader = new CsvReader(new InputStreamReader(new FileInputStream("temp.csv"), Charset.forName("UTF-8")));
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals("\"\"", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+
+    new File("temp.csv").delete();
+  }
+
+  @Test
+  public void test32() throws Exception {
+    String data = "\"Mac \"The Knife\" Peter\",\"Boswell, Jr.\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("Mac ", reader.get(0));
+    Assert.assertEquals("Boswell, Jr.", reader.get(1));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertEquals("\"Mac \"The Knife\" Peter\",\"Boswell, Jr.\"", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test33() throws Exception {
+    // tests for an old bug where an exception was
+    // thrown if Dispose was called without other methods
+    // being called. this should not throw an
+    // exception
+    String fileName = "somefile.csv";
+
+    new File(fileName).createNewFile();
+
+    try {
+      CsvReader reader = new CsvReader(fileName);
+      reader.close();
+    } finally {
+      new File(fileName).delete();
+    }
+  }
+
+  @Test
+  public void test34() throws Exception {
+    String data = "\"Chicane\", \"Love on the Run\", \"Knight Rider\", \"This field contains a comma, but it doesn't matter as the field is quoted\"\r\n"
+        + "\"Samuel Barber\", \"Adagio for Strings\", \"Classical\", \"This field contains a double quote character, \"\", but it doesn't matter as it is escaped\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("Chicane", reader.get(0));
+    Assert.assertEquals("Love on the Run", reader.get(1));
+    Assert.assertEquals("Knight Rider", reader.get(2));
+    Assert.assertEquals("This field contains a comma, but it doesn't matter as the field is quoted", reader.get(3));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(4, reader.getColumnCount());
+    Assert
+        .assertEquals(
+            "\"Chicane\", \"Love on the Run\", \"Knight Rider\", \"This field contains a comma, but it doesn't matter as the field is quoted\"",
+            reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("Samuel Barber", reader.get(0));
+    Assert.assertEquals("Adagio for Strings", reader.get(1));
+    Assert.assertEquals("Classical", reader.get(2));
+    Assert.assertEquals("This field contains a double quote character, \", but it doesn't matter as it is escaped",
+        reader.get(3));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(4, reader.getColumnCount());
+    Assert
+        .assertEquals(
+            "\"Samuel Barber\", \"Adagio for Strings\", \"Classical\", \"This field contains a double quote character, \"\", but it doesn't matter as it is escaped\"",
+            reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test35() throws Exception {
+    String data = "Chicane, Love on the Run, Knight Rider, \"This field contains a comma, but it doesn't matter as the field is quoted\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("Chicane", reader.get(0));
+    Assert.assertEquals("Love on the Run", reader.get(1));
+    Assert.assertEquals("Knight Rider", reader.get(2));
+    Assert.assertEquals("This field contains a comma, but it doesn't matter as the field is quoted", reader.get(3));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(4, reader.getColumnCount());
+    Assert
+        .assertEquals(
+            "Chicane, Love on the Run, Knight Rider, \"This field contains a comma, but it doesn't matter as the field is quoted\"",
+            reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test36() throws Exception {
+    String data = "\"some \\stuff\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("some stuff", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("\"some \\stuff\"", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test37() throws Exception {
+    String data = "  \" Chicane\"  junk here  , Love on the Run, Knight Rider, \"This field contains a comma, but it doesn't matter as the field is quoted\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(" Chicane", reader.get(0));
+    Assert.assertEquals("Love on the Run", reader.get(1));
+    Assert.assertEquals("Knight Rider", reader.get(2));
+    Assert.assertEquals("This field contains a comma, but it doesn't matter as the field is quoted", reader.get(3));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(4, reader.getColumnCount());
+    Assert
+        .assertEquals(
+            "  \" Chicane\"  junk here  , Love on the Run, Knight Rider, \"This field contains a comma, but it doesn't matter as the field is quoted\"",
+            reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test38() throws Exception {
+    String data = "1\r\n\r\n\"\"\r\n \r\n2";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("\"\"", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals(2L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals(" ", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("2", reader.get(0));
+    Assert.assertEquals(3L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("2", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test39() throws Exception {
+    CsvReader reader = CsvReader.parse("user_id,name\r\n1,Bruce");
+    Assert.assertTrue(reader.getSafetySwitch());
+    reader.setSafetySwitch(false);
+    Assert.assertFalse(reader.getSafetySwitch());
+
+    Assert.assertEquals('#', reader.getComment());
+    reader.setComment('!');
+    Assert.assertEquals('!', reader.getComment());
+
+    Assert.assertEquals(CsvReader.ESCAPE_MODE_DOUBLED, reader.getEscapeMode());
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertEquals(CsvReader.ESCAPE_MODE_BACKSLASH, reader.getEscapeMode());
+
+    Assert.assertEquals('\0', reader.getRecordDelimiter());
+    reader.setRecordDelimiter(';');
+    Assert.assertEquals(';', reader.getRecordDelimiter());
+
+    Assert.assertEquals('\"', reader.getTextQualifier());
+    reader.setTextQualifier('\'');
+    Assert.assertEquals('\'', reader.getTextQualifier());
+
+    Assert.assertTrue(reader.getTrimWhitespace());
+    reader.setTrimWhitespace(false);
+    Assert.assertFalse(reader.getTrimWhitespace());
+
+    Assert.assertFalse(reader.getUseComments());
+    reader.setUseComments(true);
+    Assert.assertTrue(reader.getUseComments());
+
+    Assert.assertTrue(reader.getUseTextQualifier());
+    reader.setUseTextQualifier(false);
+    Assert.assertFalse(reader.getUseTextQualifier());
+    reader.close();
+  }
+
+  @Test
+  public void test40() throws Exception {
+    String data = "Chicane, Love on the Run, Knight Rider, This field contains a comma\\, but it doesn't matter as the delimiter is escaped";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(false);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("Chicane", reader.get(0));
+    Assert.assertEquals("Love on the Run", reader.get(1));
+    Assert.assertEquals("Knight Rider", reader.get(2));
+    Assert
+        .assertEquals("This field contains a comma, but it doesn't matter as the delimiter is escaped", reader.get(3));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(4, reader.getColumnCount());
+    Assert
+        .assertEquals(
+            "Chicane, Love on the Run, Knight Rider, This field contains a comma\\, but it doesn't matter as the delimiter is escaped",
+            reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test41() throws Exception {
+    String data = "double\\\\\\\\double backslash";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(false);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("double\\\\double backslash", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test42() throws Exception {
+    String data = "some \\stuff";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(false);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("some stuff", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test43() throws Exception {
+    String data = "\"line 1\\nline 2\",\"line 1\\\nline 2\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("line 1\nline 2", reader.get(0));
+    Assert.assertEquals("line 1\nline 2", reader.get(1));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test44() throws Exception {
+    String data = "line 1\\nline 2,line 1\\\nline 2";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(false);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("line 1\nline 2", reader.get(0));
+    Assert.assertEquals("line 1\nline 2", reader.get(1));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test45() throws Exception {
+    String data = "\"Chicane\", \"Love on the Run\", \"Knight Rider\", \"This field contains a comma, but it doesn't matter as the field is quoted\"i"
+        + "\"Samuel Barber\", \"Adagio for Strings\", \"Classical\", \"This field contains a double quote character, \"\", but it doesn't matter as it is escaped\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    Assert.assertTrue(reader.getCaptureRawRecord());
+    reader.setCaptureRawRecord(false);
+    Assert.assertFalse(reader.getCaptureRawRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.setRecordDelimiter('i');
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("Chicane", reader.get(0));
+    Assert.assertEquals("Love on the Run", reader.get(1));
+    Assert.assertEquals("Knight Rider", reader.get(2));
+    Assert.assertEquals("This field contains a comma, but it doesn't matter as the field is quoted", reader.get(3));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(4, reader.getColumnCount());
+    Assert.assertEquals("", reader.getRawRecord());
+    Assert.assertFalse(reader.getCaptureRawRecord());
+    reader.setCaptureRawRecord(true);
+    Assert.assertTrue(reader.getCaptureRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert
+        .assertEquals(
+            "\"Samuel Barber\", \"Adagio for Strings\", \"Classical\", \"This field contains a double quote character, \"\", but it doesn't matter as it is escaped\"",
+            reader.getRawRecord());
+    Assert.assertEquals("Samuel Barber", reader.get(0));
+    Assert.assertEquals("Adagio for Strings", reader.get(1));
+    Assert.assertEquals("Classical", reader.get(2));
+    Assert.assertEquals("This field contains a double quote character, \", but it doesn't matter as it is escaped",
+        reader.get(3));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(4, reader.getColumnCount());
+    Assert.assertFalse(reader.readRecord());
+    Assert.assertTrue(reader.getCaptureRawRecord());
+    Assert.assertEquals("", reader.getRawRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test46() throws Exception {
+    String data = "Ch\\icane, Love on the Run, Kn\\ight R\\ider, Th\\is f\\ield conta\\ins an \\i\\, but \\it doesn't matter as \\it \\is escapedi"
+        + "Samuel Barber, Adag\\io for Str\\ings, Class\\ical, Th\\is f\\ield conta\\ins a comma \\, but \\it doesn't matter as \\it \\is escaped";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(false);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    reader.setRecordDelimiter('i');
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("Chicane", reader.get(0));
+    Assert.assertEquals("Love on the Run", reader.get(1));
+    Assert.assertEquals("Knight Rider", reader.get(2));
+    Assert.assertEquals("This field contains an i, but it doesn't matter as it is escaped", reader.get(3));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(4, reader.getColumnCount());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("Samuel Barber", reader.get(0));
+    Assert.assertEquals("Adagio for Strings", reader.get(1));
+    Assert.assertEquals("Classical", reader.get(2));
+    Assert.assertEquals("This field contains a comma , but it doesn't matter as it is escaped", reader.get(3));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(4, reader.getColumnCount());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test47() throws Exception {
+    byte[] buffer;
+
+    String test = "M�nchen";
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    PrintWriter writer = new PrintWriter(new OutputStreamWriter(stream, Charset.forName("UTF-8")));
+    writer.println(test);
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    CsvReader reader = new CsvReader(new InputStreamReader(new ByteArrayInputStream(buffer), Charset.forName("UTF-8")));
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(test, reader.get(0));
+    reader.close();
+  }
+
+  @Test
+  public void test48() throws Exception {
+    byte[] buffer;
+
+    String test = "M�nchen";
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    PrintWriter writer = new PrintWriter(new OutputStreamWriter(stream, Charset.forName("UTF-8")));
+    writer.write(test);
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    CsvReader reader = new CsvReader(new InputStreamReader(new ByteArrayInputStream(buffer), Charset.forName("UTF-8")));
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(test, reader.get(0));
+    reader.close();
+  }
+
+  @Test
+  public void test49() throws Exception {
+    String data = "\"\\n\\r\\t\\b\\f\\e\\v\\a\\z\\d065\\o101\\101\\x41\\u0041\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(true);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("\n\r\t\b\f\u001B\u000B\u0007zAAAAA", reader.get(0));
+    Assert.assertEquals("\"\\n\\r\\t\\b\\f\\e\\v\\a\\z\\d065\\o101\\101\\x41\\u0041\"", reader.getRawRecord());
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test50() throws Exception {
+    String data = "\\n\\r\\t\\b\\f\\e\\v\\a\\z\\d065\\o101\\101\\x41\\u0041";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(false);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("\n\r\t\b\f\u001B\u000B\u0007zAAAAA", reader.get(0));
+    Assert.assertEquals("\\n\\r\\t\\b\\f\\e\\v\\a\\z\\d065\\o101\\101\\x41\\u0041", reader.getRawRecord());
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test51() throws Exception {
+    String data = "\"\\xfa\\u0afa\\xFA\\u0AFA\"";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(true);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("\u00FA\u0AFA\u00FA\u0AFA", reader.get(0));
+    Assert.assertEquals("\"\\xfa\\u0afa\\xFA\\u0AFA\"", reader.getRawRecord());
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test52() throws Exception {
+    String data = "\\xfa\\u0afa\\xFA\\u0AFA";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setUseTextQualifier(false);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("\u00FA\u0AFA\u00FA\u0AFA", reader.get(0));
+    Assert.assertEquals("\\xfa\\u0afa\\xFA\\u0AFA", reader.getRawRecord());
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test54() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.write("1,2");
+    writer.write("3");
+    writer.write("blah \"some stuff in quotes\"");
+    writer.endRecord();
+    Assert.assertFalse(writer.getForceQualifier());
+    writer.setForceQualifier(true);
+    Assert.assertTrue(writer.getForceQualifier());
+    writer.write("1,2");
+    writer.write("3");
+    writer.write("blah \"some stuff in quotes\"");
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals(
+        "\"1,2\",3,\"blah \"\"some stuff in quotes\"\"\"\r\n\"1,2\",\"3\",\"blah \"\"some stuff in quotes\"\"\"", data);
+  }
+
+  @Test
+  public void test55() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.write("");
+    writer.write("1");
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("\"\",1", data);
+  }
+
+  @Test
+  public void test56() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, '\t', Charset.forName("ISO-8859-1"));
+    writer.write("1,2");
+    writer.write("3");
+    writer.write("blah \"some stuff in quotes\"");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("1,2\t3\t\"blah \"\"some stuff in quotes\"\"\"\r\n", data);
+  }
+
+  @Test
+  public void test57() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, '\t', Charset.forName("ISO-8859-1"));
+    Assert.assertTrue(writer.getUseTextQualifier());
+    writer.setUseTextQualifier(false);
+    Assert.assertFalse(writer.getUseTextQualifier());
+    writer.write("1,2");
+    writer.write("3");
+    writer.write("blah \"some stuff in quotes\"");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("1,2\t3\tblah \"some stuff in quotes\"\r\n", data);
+  }
+
+  @Test
+  public void test58() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, '\t', Charset.forName("ISO-8859-1"));
+    writer.write("data\r\nmore data");
+    writer.write(" 3\t", false);
+    writer.write(" 3\t");
+    writer.write(" 3\t", true);
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("\"data\r\nmore data\"\t3\t3\t\" 3\t\"\r\n", data);
+  }
+
+  @Test
+  public void test70() throws Exception {
+    String data = "\"1\",Bruce\r\n\"2\",Toni\r\n\"3\",Brian\r\n";
+
+    CsvReader reader = CsvReader.parse(data);
+    reader.setHeaders(new String[] { "userid", "name" });
+    Assert.assertEquals(2, reader.getHeaderCount());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get("userid"));
+    Assert.assertEquals("Bruce", reader.get("name"));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("2", reader.get("userid"));
+    Assert.assertEquals("Toni", reader.get("name"));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("3", reader.get("userid"));
+    Assert.assertEquals("Brian", reader.get("name"));
+    Assert.assertEquals(2L, reader.getCurrentRecord());
+    Assert.assertEquals(2, reader.getColumnCount());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test71() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.setForceQualifier(true);
+    writer.write(" data ");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("\"data\"\r\n", data);
+  }
+
+  @Test
+  public void test72() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    Assert.assertEquals('\0', writer.getRecordDelimiter());
+    writer.setRecordDelimiter(';');
+    Assert.assertEquals(';', writer.getRecordDelimiter());
+    writer.write("a;b");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("\"a;b\";", data);
+  }
+
+  @Test
+  public void test73() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    Assert.assertEquals(CsvWriter.ESCAPE_MODE_DOUBLED, writer.getEscapeMode());
+    writer.setEscapeMode(CsvWriter.ESCAPE_MODE_BACKSLASH);
+    Assert.assertEquals(CsvWriter.ESCAPE_MODE_BACKSLASH, writer.getEscapeMode());
+    writer.write("1,2");
+    writer.write("3");
+    writer.write("blah \"some stuff in quotes\"");
+    writer.endRecord();
+    writer.setForceQualifier(true);
+    writer.write("1,2");
+    writer.write("3");
+    writer.write("blah \"some stuff in quotes\"");
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+    Assert.assertEquals(
+        "\"1,2\",3,\"blah \\\"some stuff in quotes\\\"\"\r\n\"1,2\",\"3\",\"blah \\\"some stuff in quotes\\\"\"", data);
+  }
+
+  @Test
+  public void test74() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.setEscapeMode(CsvWriter.ESCAPE_MODE_BACKSLASH);
+    writer.setUseTextQualifier(false);
+    writer.write("1,2");
+    writer.write("3");
+    writer.write("blah \"some stuff in quotes\"");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+    Assert.assertEquals("1\\,2,3,blah \"some stuff in quotes\"\r\n", data);
+  }
+
+  @Test
+  public void test75() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.write("1");
+    writer.endRecord();
+    writer.writeComment("blah");
+    writer.write("2");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+    Assert.assertEquals("1\r\n#blah\r\n2\r\n", data);
+  }
+
+  @Test
+  public void test76() throws Exception {
+    CsvReader reader = CsvReader.parse("user_id,name\r\n1,Bruce");
+    Assert.assertNull(reader.getHeaders());
+    Assert.assertEquals(-1, reader.getIndex("user_id"));
+    Assert.assertEquals("", reader.getHeader(0));
+    Assert.assertTrue(reader.readHeaders());
+    Assert.assertEquals(0, reader.getIndex("user_id"));
+    Assert.assertEquals("user_id", reader.getHeader(0));
+    String[] headers = reader.getHeaders();
+    Assert.assertEquals(2, headers.length);
+    Assert.assertEquals("user_id", headers[0]);
+    Assert.assertEquals("name", headers[1]);
+    reader.setHeaders(null);
+    Assert.assertNull(reader.getHeaders());
+    Assert.assertEquals(-1, reader.getIndex("user_id"));
+    Assert.assertEquals("", reader.getHeader(0));
+    reader.close();
+  }
+
+  @Test
+  public void test77() {
+    try {
+      CsvReader.parse(null);
+    } catch (Exception ex) {
+      assertException(new IllegalArgumentException("Parameter data can not be null."), ex);
+    }
+  }
+
+  @Test
+  public void test78() throws Exception {
+    CsvReader reader = CsvReader.parse("1,Bruce");
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertFalse(reader.isQualified(999));
+    reader.close();
+  }
+
+  @Test
+  public void test79() {
+    CsvReader reader;
+    reader = CsvReader.parse("");
+    reader.close();
+    try {
+      reader.readRecord();
+    } catch (Exception ex) {
+      assertException(new IOException("This instance of the CsvReader class has already been closed."), ex);
+    }
+  }
+
+  @Test
+  public void test81() throws Exception {
+    CsvReader reader = CsvReader.parse(generateString('a', 100001));
+    try {
+      reader.readRecord();
+    } catch (Exception ex) {
+      assertException(
+          new IOException(
+              "Maximum column length of 100,000 exceeded in column 0 in record 0. Set the SafetySwitch property to false if you're expecting column lengths greater than 100,000 characters to avoid this error."),
+          ex);
+    }
+    reader.close();
+  }
+
+  @Test
+  public void test82() throws Exception {
+    StringBuilder holder = new StringBuilder(200010);
+
+    for (int i = 0; i < 100000; i++) {
+      holder.append("a,");
+    }
+
+    holder.append("a");
+
+    CsvReader reader = CsvReader.parse(holder.toString());
+    try {
+      reader.readRecord();
+    } catch (Exception ex) {
+      assertException(
+          new IOException(
+              "Maximum column count of 100,000 exceeded in record 0. Set the SafetySwitch property to false if you're expecting more than 100,000 columns per record to avoid this error."),
+          ex);
+    }
+    reader.close();
+  }
+
+  @Test
+  public void test83() throws Exception {
+    CsvReader reader = CsvReader.parse(generateString('a', 100001));
+    reader.setSafetySwitch(false);
+    reader.readRecord();
+    reader.close();
+  }
+
+  @Test
+  public void test84() throws Exception {
+    StringBuilder holder = new StringBuilder(200010);
+
+    for (int i = 0; i < 100000; i++) {
+      holder.append("a,");
+    }
+
+    holder.append("a");
+
+    CsvReader reader = CsvReader.parse(holder.toString());
+    reader.setSafetySwitch(false);
+    reader.readRecord();
+    reader.close();
+  }
+
+  @Test
+  public void test85() throws Exception {
+    CsvReader reader = CsvReader.parse(generateString('a', 100000));
+    reader.readRecord();
+    reader.close();
+  }
+
+  @Test
+  public void test86() throws Exception {
+    StringBuilder holder = new StringBuilder(200010);
+
+    for (int i = 0; i < 99999; i++) {
+      holder.append("a,");
+    }
+
+    holder.append("a");
+
+    CsvReader reader = CsvReader.parse(holder.toString());
+    reader.readRecord();
+    reader.close();
+  }
+
+  @Test
+  public void test87() throws Exception {
+    CsvWriter writer = new CsvWriter("temp.csv");
+    writer.write("1");
+    writer.close();
+
+    CsvReader reader = new CsvReader("temp.csv");
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+
+    new File("temp.csv").delete();
+  }
+
+  @SuppressWarnings("unused")
+  @Test
+  public void test88() throws Exception {
+    try {
+      CsvReader reader = new CsvReader((String) null, ',', Charset.forName("ISO-8859-1"));
+    } catch (Exception ex) {
+      assertException(new IllegalArgumentException("Parameter fileName can not be null."), ex);
+    }
+  }
+
+  @SuppressWarnings("unused")
+  @Test
+  public void test89() throws Exception {
+    try {
+      CsvReader reader = new CsvReader("temp.csv", ',', null);
+    } catch (Exception ex) {
+      assertException(new IllegalArgumentException("Parameter charset can not be null."), ex);
+    }
+  }
+
+  @SuppressWarnings("unused")
+  @Test
+  public void test90() throws Exception {
+    try {
+      CsvReader reader = new CsvReader((Reader) null, ',');
+    } catch (Exception ex) {
+      assertException(new IllegalArgumentException("Parameter inputStream can not be null."), ex);
+    }
+  }
+
+  @Test
+  public void test91() throws Exception {
+    byte[] buffer;
+
+    String test = "test";
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    PrintWriter writer = new PrintWriter(stream);
+    writer.println(test);
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    CsvReader reader = new CsvReader(new ByteArrayInputStream(buffer), Charset.forName("ISO-8859-1"));
+    reader.readRecord();
+    Assert.assertEquals(test, reader.get(0));
+    reader.close();
+  }
+
+  @Test
+  public void test92() throws Exception {
+    byte[] buffer;
+
+    String test = "test";
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    PrintWriter writer = new PrintWriter(stream);
+    writer.println(test);
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    CsvReader reader = new CsvReader(new ByteArrayInputStream(buffer), ',', Charset.forName("ISO-8859-1"));
+    reader.readRecord();
+    Assert.assertEquals(test, reader.get(0));
+    reader.close();
+  }
+
+  @SuppressWarnings("unused")
+  @Test
+  public void test112() throws Exception {
+    try {
+      CsvWriter writer = new CsvWriter((String) null, ',', Charset.forName("ISO-8859-1"));
+    } catch (Exception ex) {
+      assertException(new IllegalArgumentException("Parameter fileName can not be null."), ex);
+    }
+  }
+
+  @SuppressWarnings("unused")
+  @Test
+  public void test113() throws Exception {
+    try {
+      CsvWriter writer = new CsvWriter("test.csv", ',', (Charset) null);
+    } catch (Exception ex) {
+      assertException(new IllegalArgumentException("Parameter charset can not be null."), ex);
+    }
+  }
+
+  @SuppressWarnings("unused")
+  @Test
+  public void test114() throws Exception {
+    try {
+      CsvWriter writer = new CsvWriter((Writer) null, ',');
+    } catch (Exception ex) {
+      assertException(new IllegalArgumentException("Parameter outputStream can not be null."), ex);
+    }
+  }
+
+  @Test
+  public void test115() throws Exception {
+    try {
+      CsvWriter writer = new CsvWriter("test.csv");
+
+      writer.close();
+
+      writer.write("");
+    } catch (Exception ex) {
+      assertException(new IOException("This instance of the CsvWriter class has already been closed."), ex);
+    }
+  }
+
+  @Test
+  public void test117() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    Assert.assertEquals('#', writer.getComment());
+    writer.setComment('~');
+    Assert.assertEquals('~', writer.getComment());
+
+    writer.setRecordDelimiter(';');
+
+    writer.write("1");
+    writer.endRecord();
+    writer.writeComment("blah");
+
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("1;~blah;", data);
+  }
+
+  @Test
+  public void test118() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, '\t', Charset.forName("ISO-8859-1"));
+    Assert.assertEquals('\"', writer.getTextQualifier());
+    writer.setTextQualifier('\'');
+    Assert.assertEquals('\'', writer.getTextQualifier());
+
+    writer.write("1,2");
+    writer.write("3");
+    writer.write("blah \"some stuff in quotes\"");
+    writer.write("blah \'some stuff in quotes\'");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("1,2\t3\tblah \"some stuff in quotes\"\t\'blah \'\'some stuff in quotes\'\'\'\r\n", data);
+  }
+
+  @Test
+  public void test119() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.write("1,2");
+    writer.write("3");
+    writer.endRecord();
+
+    Assert.assertEquals(',', writer.getDelimiter());
+    writer.setDelimiter('\t');
+    Assert.assertEquals('\t', writer.getDelimiter());
+
+    writer.write("1,2");
+    writer.write("3");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("\"1,2\",3\r\n1,2\t3\r\n", data);
+  }
+
+  @Test
+  public void test120() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.write("1,2");
+    writer.endRecord();
+
+    buffer = stream.toByteArray();
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+    Assert.assertEquals("", data);
+
+    writer.flush(); // testing that flush flushed to stream
+
+    buffer = stream.toByteArray();
+    stream.close();
+    data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+    Assert.assertEquals("\"1,2\"\r\n", data);
+    writer.close();
+  }
+
+  @Test
+  public void test121() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.writeRecord(new String[] { " 1 ", "2" }, false);
+    writer.writeRecord(new String[] { " 1 ", "2" });
+    writer.writeRecord(new String[] { " 1 ", "2" }, true);
+    writer.writeRecord(new String[0], true);
+    writer.writeRecord(null, true);
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+    Assert.assertEquals("1,2\r\n1,2\r\n\" 1 \",2\r\n", data);
+  }
+
+  @Test
+  public void test122() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.write("1,2");
+    writer.write(null);
+    writer.write("3 ", true);
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("\"1,2\",,\"3 \"\r\n", data);
+  }
+
+  @Test
+  public void test123() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.write("#123");
+    writer.endRecord();
+
+    writer.setEscapeMode(CsvWriter.ESCAPE_MODE_BACKSLASH);
+    writer.setUseTextQualifier(false);
+
+    writer.write("#123");
+    writer.endRecord();
+
+    writer.write("#");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("\"#123\"\r\n\\#123\r\n\\#\r\n", data);
+  }
+
+  @Test
+  public void test124() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.setRecordDelimiter(';');
+    writer.setUseTextQualifier(false);
+    writer.setEscapeMode(CsvWriter.ESCAPE_MODE_BACKSLASH);
+
+    writer.write("1;2");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("1\\;2;", data);
+  }
+
+  @Test
+  public void test131() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.setUseTextQualifier(false);
+    writer.setEscapeMode(CsvWriter.ESCAPE_MODE_BACKSLASH);
+
+    writer.write("1,\\\r\n2");
+    writer.endRecord();
+
+    writer.setRecordDelimiter(';');
+
+    writer.write("1,\\;2");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("1\\,\\\\\\\r\\\n2\r\n1\\,\\\\\\;2;", data);
+  }
+
+  @Test
+  public void test132() throws Exception {
+    byte[] buffer;
+
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    CsvWriter writer = new CsvWriter(stream, ',', Charset.forName("ISO-8859-1"));
+    writer.setEscapeMode(CsvWriter.ESCAPE_MODE_BACKSLASH);
+
+    writer.write("1,\\2");
+    writer.endRecord();
+    writer.close();
+
+    buffer = stream.toByteArray();
+    stream.close();
+
+    String data = Charset.forName("ISO-8859-1").decode(ByteBuffer.wrap(buffer)).toString();
+
+    Assert.assertEquals("\"1,\\\\2\"\r\n", data);
+  }
+
+  @Test
+  public void test135() throws Exception {
+    CsvReader reader = CsvReader.parse("1\n\n1\r\r1\r\n\r\n1\n\r1");
+    Assert.assertTrue(reader.getSkipEmptyRecords());
+    reader.setSkipEmptyRecords(false);
+    Assert.assertFalse(reader.getSkipEmptyRecords());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(2L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals(3L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(4L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals(5L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(6L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals(7L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(8L, reader.getCurrentRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test136() throws Exception {
+    CsvReader reader = CsvReader.parse("1\n\n1\r\r1\r\n\r\n1\n\r1");
+    Assert.assertTrue(reader.getSkipEmptyRecords());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(2L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(3L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(4L, reader.getCurrentRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test137() throws Exception {
+    CsvReader reader = CsvReader.parse("1;; ;1");
+    reader.setRecordDelimiter(';');
+    Assert.assertTrue(reader.getSkipEmptyRecords());
+    reader.setSkipEmptyRecords(false);
+    Assert.assertFalse(reader.getSkipEmptyRecords());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals(2L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(3L, reader.getCurrentRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test138() throws Exception {
+    CsvReader reader = CsvReader.parse("1;; ;1");
+    reader.setRecordDelimiter(';');
+    Assert.assertTrue(reader.getSkipEmptyRecords());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(0L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("", reader.get(0));
+    Assert.assertEquals(1L, reader.getCurrentRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(1, reader.getColumnCount());
+    Assert.assertEquals("1", reader.get(0));
+    Assert.assertEquals(2L, reader.getCurrentRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test143() throws Exception {
+    CsvReader reader = CsvReader.parse("\"" + generateString('a', 100001) + "\"");
+    try {
+      reader.readRecord();
+    } catch (Exception ex) {
+      assertException(
+          new IOException(
+              "Maximum column length of 100,000 exceeded in column 0 in record 0. Set the SafetySwitch property to false if you're expecting column lengths greater than 100,000 characters to avoid this error."),
+          ex);
+    }
+    reader.close();
+  }
+
+  @Test
+  public void test144() throws Exception {
+    CsvReader reader = CsvReader.parse("\"" + generateString('a', 100000) + "\"");
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(generateString('a', 100000), reader.get(0));
+    Assert.assertEquals("\"" + generateString('a', 100000) + "\"", reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test145() throws Exception {
+    CsvReader reader = CsvReader.parse("\"" + generateString('a', 100001) + "\"");
+    reader.setSafetySwitch(false);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(generateString('a', 100001), reader.get(0));
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test146() throws Exception {
+    // testing SkipLine's buffer
+    CsvReader reader = CsvReader.parse("\"" + generateString('a', 10000) + "\r\nb");
+    Assert.assertEquals("", reader.getRawRecord());
+    Assert.assertTrue(reader.skipLine());
+    Assert.assertEquals("", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals("b", reader.get(0));
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test147() throws Exception {
+    // testing AppendLetter's buffer
+    StringBuilder data = new StringBuilder(20000);
+    for (int i = 0; i < 10000; i++) {
+      data.append("\\b");
+    }
+
+    CsvReader reader = CsvReader.parse(data.toString());
+    reader.setUseTextQualifier(false);
+    reader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(generateString('\b', 10000), reader.get(0));
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @Test
+  public void test148() throws Exception {
+    // testing a specific case in GetRawRecord where the result is what's in
+    // the data buffer
+    // plus what's in the raw buffer
+    CsvReader reader = CsvReader.parse("\"" + generateString('a', 100000) + "\"\r\n" + generateString('a', 100000));
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(generateString('a', 100000), reader.get(0));
+    Assert.assertEquals("\"" + generateString('a', 100000) + "\"", reader.getRawRecord());
+    Assert.assertTrue(reader.readRecord());
+    Assert.assertEquals(generateString('a', 100000), reader.get(0));
+    Assert.assertEquals(generateString('a', 100000), reader.getRawRecord());
+    Assert.assertFalse(reader.readRecord());
+    reader.close();
+  }
+
+  @SuppressWarnings("unused")
+  @Test
+  public void test149() throws Exception {
+    try {
+      CsvReader reader = new CsvReader("C:\\somefilethatdoesntexist.csv");
+    } catch (Exception ex) {
+      assertException(new FileNotFoundException("File C:\\somefilethatdoesntexist.csv does not exist."), ex);
+    }
+  }
+
+  @Test
+  public void test173() throws Exception {
+    FailingReader fail = new FailingReader();
+
+    CsvReader reader = new CsvReader(fail);
+    boolean exceptionThrown = false;
+
+    Assert.assertFalse(fail.DisposeCalled);
+    try {
+      // need to test IO exception block logic while trying to read
+      reader.readRecord();
+    } catch (IOException ex) {
+      // make sure stream that caused exception
+      // has been sent a dipose call
+      Assert.assertTrue(fail.DisposeCalled);
+      exceptionThrown = true;
+      Assert.assertEquals("Read failed.", ex.getMessage());
+    } finally {
+      reader.close();
+    }
+
+    Assert.assertTrue(exceptionThrown);
+
+    // test to make sure object has been marked
+    // internally as disposed
+    try {
+      reader.getHeaders();
+    } catch (Exception ex) {
+      assertException(new IOException("This instance of the CsvReader class has already been closed."), ex);
+    }
+  }
+
+  private class FailingReader extends Reader {
+    public boolean DisposeCalled = false;
+
+    public FailingReader() {
+      super("");
+    }
+
+    public int read(char[] buffer, int index, int count) throws IOException {
+      throw new IOException("Read failed.");
+    }
+
+    public void close() {
+      DisposeCalled = true;
+    }
+  }
+}

+ 34 - 0
src/test/java/de/mcs/utils/TestTimeHelper.java

@@ -0,0 +1,34 @@
+package de.mcs.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class TestTimeHelper {
+
+  @Test
+  public void test() {
+    long value = 234;
+    long expected = value * 1000;
+
+    assertEquals(expected, TimeHelper.sec2millis(value));
+    expected = expected * 60;
+    assertEquals(expected, TimeHelper.min2millis(value));
+    expected = expected * 60;
+    assertEquals(expected, TimeHelper.hour2millis(value));
+
+    value = 234 * 1000 * 60 * 60;
+    expected = value / 1000;
+    assertEquals(expected, TimeHelper.millis2sec(value));
+    expected = expected / 60;
+    assertEquals(expected, TimeHelper.millis2min(value));
+    expected = expected / 60;
+    assertEquals(expected, TimeHelper.millis2hour(value));
+
+    value = 234;
+    expected = value * 60;
+    assertEquals(expected, TimeHelper.min2sec(value));
+    expected = expected * 60;
+    assertEquals(expected, TimeHelper.hour2sec(value));
+  }
+}

+ 212 - 0
src/test/java/de/mcs/utils/TestVersion.java

@@ -0,0 +1,212 @@
+/**
+ * 
+ */
+package de.mcs.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class TestVersion {
+
+  class GreatLessVersionTupel {
+    Version greatVersion;
+    Version lessVersion;
+
+    public GreatLessVersionTupel(Version greatVersion, Version lessVersion) {
+      this.greatVersion = greatVersion;
+      this.lessVersion = lessVersion;
+    }
+  }
+
+  class VersionFormat {
+    String versionString;
+    int major;
+    int minor;
+    int release;
+    int build;
+    private String versionState;
+
+    public VersionFormat(String versionString, int major, int minor, int release, int build) {
+      this.versionString = versionString;
+      this.build = build;
+      this.major = major;
+      this.minor = minor;
+      this.release = release;
+    }
+
+    public VersionFormat(String versionString, int major, int minor, int release, int build, String versionStatus) {
+      this.versionString = versionString;
+      this.build = build;
+      this.major = major;
+      this.minor = minor;
+      this.release = release;
+      this.versionState = versionStatus;
+    }
+
+    public boolean hasVersionState() {
+      return (versionState != null) && !versionState.equals("");
+    }
+  }
+
+  VersionFormat[] versionFormats = { new VersionFormat("1", 1, -1, -1, -1), new VersionFormat("1.0", 1, 0, -1, -1),
+      new VersionFormat("1.0.0", 1, 0, 0, -1), new VersionFormat("1.0.0.0", 1, 0, 0, 0),
+      new VersionFormat("2.1", 2, 1, -1, -1), new VersionFormat("2.3.5.0054", 2, 3, 5, 54),
+      new VersionFormat("2 - SNAPSHOT", 2, -1, -1, -1, "SNAPSHOT"),
+      new VersionFormat("2.1- RELEASE", 2, 1, -1, -1, "RELEASE"),
+      new VersionFormat("2.1.7 -SNAPSHOT", 2, 1, 7, -1, "SNAPSHOT"),
+      new VersionFormat("2.1.4.5-VERSIONSTATE", 2, 1, 4, 5, "VERSIONSTATE"),
+      new VersionFormat("2.1.4.5-", 2, 1, 4, 5, "") };
+
+  /**
+   * Test method for {@link de.mcs.utils.Version#Version(java.lang.String)}.
+   */
+  @Test
+  public void testVersionString() {
+    for (VersionFormat version : versionFormats) {
+      Version versionUnderTest = new Version(version.versionString);
+      assertEquals(version.major, versionUnderTest.getMajor());
+      assertEquals(version.minor, versionUnderTest.getMinor());
+      assertEquals(version.release, versionUnderTest.getRelease());
+      assertEquals(version.build, versionUnderTest.getBuild());
+      if (version.hasVersionState()) {
+        assertTrue(versionUnderTest.hasVersionState());
+        assertEquals(version.versionState, versionUnderTest.getVersionState());
+      }
+    }
+  }
+
+  /**
+   * Test method for {@link de.mcs.utils.Version#incrementMajor()}.
+   */
+  @Test
+  public void testIncrementAll() {
+    for (VersionFormat versionFormat : versionFormats) {
+      Version version = new Version(versionFormat.versionString);
+      assertEquals(versionFormat.major, version.getMajor());
+      version.incrementMajor();
+      assertEquals(versionFormat.major + 1, version.getMajor());
+      version.incrementMajor();
+      assertEquals(versionFormat.major + 2, version.getMajor());
+
+      assertEquals(versionFormat.minor, version.getMinor());
+      version.incrementMinor();
+      assertEquals(versionFormat.minor + 1, version.getMinor());
+      version.incrementMinor();
+      assertEquals(versionFormat.minor + 2, version.getMinor());
+
+      assertEquals(versionFormat.release, version.getRelease());
+      version.incrementRelease();
+      assertEquals(versionFormat.release + 1, version.getRelease());
+      version.incrementRelease();
+      assertEquals(versionFormat.release + 2, version.getRelease());
+
+      assertEquals(versionFormat.build, version.getBuild());
+      version.incrementBuild();
+      assertEquals(versionFormat.build + 1, version.getBuild());
+      version.incrementBuild();
+      assertEquals(versionFormat.build + 2, version.getBuild());
+    }
+  }
+
+  /**
+   * Test method for {@link de.mcs.utils.Version#decrementMajor()}.
+   */
+  @Test
+  public void testDecrementAll() {
+    for (VersionFormat versionFormat : versionFormats) {
+      Version version = new Version(versionFormat.versionString);
+      assertEquals(versionFormat.major, version.getMajor());
+      version.decrementMajor();
+      assertEquals(versionFormat.major - 1, version.getMajor());
+      version.decrementMajor();
+      assertEquals(versionFormat.major - 2, version.getMajor());
+
+      assertEquals(versionFormat.minor, version.getMinor());
+      version.decrementMinor();
+      assertEquals(versionFormat.minor - 1, version.getMinor());
+      version.decrementMinor();
+      assertEquals(versionFormat.minor - 2, version.getMinor());
+
+      assertEquals(versionFormat.release, version.getRelease());
+      version.decrementRelease();
+      assertEquals(versionFormat.release - 1, version.getRelease());
+      version.decrementRelease();
+      assertEquals(versionFormat.release - 2, version.getRelease());
+
+      assertEquals(versionFormat.build, version.getBuild());
+      version.decrementBuild();
+      assertEquals(versionFormat.build - 1, version.getBuild());
+      version.decrementBuild();
+      assertEquals(versionFormat.build - 2, version.getBuild());
+    }
+  }
+
+  /**
+   * Test method for
+   * {@link de.mcs.utils.Version#greaterThan(de.mcs.utils.Version)}.
+   */
+  @Test
+  public void testGreaterLesserThanEquals() {
+    GreatLessVersionTupel[] greatLessVersionTupels = new GreatLessVersionTupel[] {
+        new GreatLessVersionTupel(new Version("2"), new Version("1")),
+        new GreatLessVersionTupel(new Version("2.0"), new Version("1.0")),
+        new GreatLessVersionTupel(new Version("2.0.0"), new Version("1.0.0")),
+        new GreatLessVersionTupel(new Version("2.0.0.0"), new Version("1.0.0.0")),
+        new GreatLessVersionTupel(new Version("2.1"), new Version("2.0")),
+        new GreatLessVersionTupel(new Version("2.1"), new Version("2.0.0")),
+        new GreatLessVersionTupel(new Version("2.1"), new Version("2.0.0.0")),
+        new GreatLessVersionTupel(new Version("2.0.1"), new Version("2.0.0")),
+        new GreatLessVersionTupel(new Version("2.0.1"), new Version("2.0.0.0")),
+        new GreatLessVersionTupel(new Version("2.0.0.1"), new Version("2.0.0.0")),
+        new GreatLessVersionTupel(new Version("2"), new Version("2-SNAPSHOT")),
+        new GreatLessVersionTupel(new Version("2.0"), new Version("2.0-SNAPSHOT")),
+        new GreatLessVersionTupel(new Version("2.1-SNAPSHOT"), new Version("2.0-SNAPSHOT")),
+        new GreatLessVersionTupel(new Version("2.1-RELEASE"), new Version("2.1-SNAPSHOT")),
+        new GreatLessVersionTupel(new Version("2"), new Version("1")) };
+
+    for (GreatLessVersionTupel greatLessVersionTupel : greatLessVersionTupels) {
+      Version lessVersion = greatLessVersionTupel.lessVersion;
+      Version greatVersion = greatLessVersionTupel.greatVersion;
+
+      assertTrue(greatVersion.greaterThan(lessVersion));
+      assertFalse(lessVersion.greaterThan(greatVersion));
+
+      assertTrue(lessVersion.lesserThan(greatVersion));
+      assertFalse(greatVersion.lesserThan(lessVersion));
+
+      assertFalse(greatVersion.equals(lessVersion));
+      assertFalse(lessVersion.equals(greatVersion));
+
+      assertTrue(greatVersion.equals(greatVersion));
+      assertTrue(lessVersion.equals(lessVersion));
+    }
+  }
+
+  @Test
+  public void quickTest() {
+    GreatLessVersionTupel greatLessVersionTupel = new GreatLessVersionTupel(new Version("2.1"),
+        new Version("2-SNAPSHOT"));
+
+    Version lessVersion = greatLessVersionTupel.lessVersion;
+    Version greatVersion = greatLessVersionTupel.greatVersion;
+
+    assertTrue(greatVersion.greaterThan(lessVersion));
+    assertFalse(lessVersion.greaterThan(greatVersion));
+
+    assertTrue(lessVersion.lesserThan(greatVersion));
+    assertFalse(greatVersion.lesserThan(lessVersion));
+
+    assertFalse(greatVersion.equals(lessVersion));
+    assertFalse(lessVersion.equals(greatVersion));
+
+    assertTrue(greatVersion.equals(greatVersion));
+    assertTrue(lessVersion.equals(lessVersion));
+  }
+}

+ 72 - 0
src/test/java/de/mcs/utils/TstNumberHelper.java

@@ -0,0 +1,72 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2007 by MCS
+ * -------------------------------------- Created on 24.01.2007 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/**
+ * 
+ */
+package de.mcs.utils;
+
+import junit.framework.TestCase;
+
+/**
+ * @author w.klaas
+ * 
+ */
+public class TstNumberHelper extends TestCase {
+
+    /**
+     * Test method for
+     * {@link de.mcs.utils.NumberHelper#parseInt(java.lang.String)}.
+     */
+    public void testParseInt() {
+        assertEquals(1024, NumberHelper.parseInt("1K"));
+        assertEquals(2048, NumberHelper.parseInt("2K"));
+
+        assertEquals(1024 * 1024, NumberHelper.parseInt("1M"));
+        assertEquals(2048 * 1024, NumberHelper.parseInt("2M"));
+
+        assertEquals(1024 * 1024 * 1024, NumberHelper.parseInt("1G"));
+        assertEquals(2048 * 1024 * 1024, NumberHelper.parseInt("2G"));
+
+        assertEquals(1000, NumberHelper.parseInt("1k"));
+        assertEquals(2000, NumberHelper.parseInt("2k"));
+
+        assertEquals(1000 * 1000, NumberHelper.parseInt("1m"));
+        assertEquals(2000 * 1000, NumberHelper.parseInt("2m"));
+
+        assertEquals(1000 * 1000 * 1000, NumberHelper.parseInt("1g"));
+        assertEquals(2000 * 1000 * 1000, NumberHelper.parseInt("2g"));
+    }
+
+    /**
+     * Test method for
+     * {@link de.mcs.utils.NumberHelper#parseTime(java.lang.String)}.
+     */
+    public void testParseTime() {
+        assertEquals(1000, NumberHelper.parseTime("1s"));
+        assertEquals(1000 * 2, NumberHelper.parseTime("2s"));
+
+        assertEquals(1000 * 60, NumberHelper.parseTime("1m"));
+        assertEquals(1000 * 2 * 60, NumberHelper.parseTime("2m"));
+
+        assertEquals(1000 * 60 * 60, NumberHelper.parseTime("1h"));
+        assertEquals(1000 * 2 * 60 * 60, NumberHelper.parseTime("2h"));
+
+        assertEquals(1000 * 60 * 60 * 24, NumberHelper.parseTime("1D"));
+        assertEquals(1000 * 2 * 60 * 60 * 24, NumberHelper.parseTime("2D"));
+    }
+
+}

+ 72 - 0
src/test/java/de/mcs/utils/TstStringUtils.java

@@ -0,0 +1,72 @@
+/*
+ * MCS Media Computer Software Copyright (c) 2006 by MCS
+ * -------------------------------------- Created on 21.09.2006 by w.klaas
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/**
+ * 
+ */
+package de.mcs.utils;
+
+import junit.framework.TestCase;
+
+/**
+ * @author w.klaas
+ * 
+ */
+public class TstStringUtils extends TestCase {
+
+  /**
+   * Test method for
+   * {@link de.mcs.utils.StringUtils#md5String(java.lang.String)}.
+   */
+  public void testMd5String() {
+    String data = "Das ist das Haus vom Nikolaus";
+    String md5 = StringFormat.md5String(data);
+    assertNotNull(md5);
+    System.out.println(md5);
+  }
+
+  /**
+   * Test method for
+   * {@link de.mcs.utils.StringUtils#md5String(java.lang.String)}.
+   */
+  public void testShaString() {
+    String data = "Das ist das Haus vom Nikolaus";
+    String sha = StringFormat.shaString(data);
+    assertNotNull(sha);
+    System.out.println(sha);
+  }
+
+  /**
+   * Test method for
+   * {@link de.mcs.utils.StringUtils#md5StringBytes(java.lang.String)}.
+   */
+  public void testMd5StringBytes() {
+    String data = "Das ist das Haus vom Nikolaus";
+    byte[] md5 = StringUtils.md5StringBytes(data);
+    assertNotNull(md5);
+    System.out.println(md5);
+  }
+
+  public void testCsvToArray() {
+    String test = "de=neu,\"en=haste \"\"nicht\",gesehen\",\"default=murks\"";
+    String[] csvStringToArray = StringUtils.csvStringToArray(test, ',', '"');
+    System.out.println(csvStringToArray.length);
+    for (String string : csvStringToArray) {
+      System.out.println(string);
+    }
+  }
+
+}

+ 100 - 0
src/test/java/de/mcs/utils/caches/TestLocalCache.java

@@ -0,0 +1,100 @@
+/**
+ * 
+ */
+package de.mcs.utils.caches;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import de.mcs.utils.StringUtils;
+
+/**
+ * @author wklaa_000
+ *
+ */
+public class TestLocalCache {
+
+  private class CacheEntry extends TimedCacheEntry {
+
+    private boolean result;
+
+    /**
+     * @param key
+     * @param b
+     */
+    private CacheEntry(String key, boolean b) {
+      super(key);
+      this.result = b;
+    }
+
+  }
+
+  private static final int CACHE_INVALIDATION_TIME = 5 * 60 * 1000;
+
+  private static final TimedCache<CacheEntry> localCache = new TimedCache<>();
+
+  /**
+   * @throws java.lang.Exception
+   */
+  @Before
+  public void setUp() throws Exception {
+    localCache.clear();
+  }
+
+  @Test
+  public void test() throws InterruptedException {
+    // fill up local cache
+    System.out.println("fill up cache");
+    for (int i = 0; i < 50000; i++) {
+      String cacheKey = createCacheKey(i);
+
+      CacheEntry entry = new CacheEntry(cacheKey, true);
+      entry.setTime(System.currentTimeMillis() - CACHE_INVALIDATION_TIME);
+      localCache.put(entry);
+    }
+
+    for (int i = 50000; i < 100000; i++) {
+      String cacheKey = createCacheKey(i);
+
+      CacheEntry entry = new CacheEntry(cacheKey, true);
+      localCache.put(entry);
+    }
+
+    System.out.println("test cache");
+    for (int i = 0; i < 100000; i++) {
+      String cacheKey = createCacheKey(i);
+      CacheEntry entry = localCache.get(cacheKey);
+      assertNotNull(entry);
+
+      if (i >= 50000) {
+        assertTrue((entry.getTime() + CACHE_INVALIDATION_TIME) > System.currentTimeMillis());
+      } else {
+        assertFalse((entry.getTime() + CACHE_INVALIDATION_TIME) > System.currentTimeMillis());
+      }
+
+    }
+
+    // boolean result = authenticateUser(r, username, password, config);
+
+    localCache.asyncRemoveEntries(CACHE_INVALIDATION_TIME);
+    localCache.asyncRemoveEntries(CACHE_INVALIDATION_TIME);
+
+    assertEquals(1, localCache.getCount());
+    Thread.sleep(1000);
+    assertEquals(50000, localCache.size());
+
+  }
+
+  private String createCacheKey(int i) {
+    String username = String.format("Hallo%d", i);
+    String password = String.format("pwd%d", i);
+    String cacheKey = StringUtils.md5String(String.format("%s_%s", username, password));
+    return cacheKey;
+  };
+
+}

+ 77 - 0
src/test/java/de/mcs/utils/caches/TestObjectCache.java

@@ -0,0 +1,77 @@
+package de.mcs.utils.caches;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import de.mcs.utils.caches.ObjectCache.TIMEMODE;
+
+public class TestObjectCache extends TestCase {
+
+  @Test
+  public void test() {
+    List<String> list = new ArrayList<String>();
+    ObjectCache<Object> cache = new ObjectCache<Object>(10);
+
+    for (int i = 1; i < 11; i++) {
+      list.add(cache.addObject(new Integer(i)));
+      try {
+        Thread.sleep(100);
+      } catch (InterruptedException e) {
+        e.printStackTrace();
+      }
+    }
+    assertEquals(10, cache.getCacheCount());
+
+    list.add(cache.addObject(new Integer(11)));
+    try {
+      Thread.sleep(100);
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+    assertEquals(10, cache.getCacheCount());
+
+    String first = list.get(0);
+    Object object = cache.getObject(first);
+    // assertNull(object);
+
+    for (int i = 1; i < 11; i++) {
+      String id = list.get(i);
+      object = cache.getObject(id);
+      // assertNotNull(object);
+    }
+
+  }
+
+  @Test
+  public void testAutoRemoveCreate() throws InterruptedException {
+    ObjectCache<String> objectCache = new ObjectCache<>();
+    objectCache.setTimemode(TIMEMODE.CREATION);
+
+    List<String> ids = new ArrayList<>();
+
+    for (int i = 0; i < 100; i++) {
+      String entry = String.format("string%d", i);
+      String sId = objectCache.addObject(entry);
+      ids.add(sId);
+    }
+    assertEquals(100, objectCache.size());
+
+    System.out.println("wait");
+    Thread.sleep(500);
+
+    System.out.println("start cleanup task");
+    objectCache.startCleanupTask(1, "cleanup");
+
+    assertEquals(100, objectCache.size());
+
+    System.out.println("wait for removal.");
+    Thread.sleep(2000);
+
+    System.out.println("everything should be removed.");
+    assertEquals(0, objectCache.size());
+  }
+}

+ 110 - 0
src/test/java/de/mcs/utils/codecs/TestBase64.java

@@ -0,0 +1,110 @@
+package de.mcs.utils.codecs;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Random;
+
+import junit.framework.TestCase;
+import de.mcs.utils.StreamHelper;
+
+public class TestBase64 extends TestCase {
+
+    public void testBase64EncodeDecode() {
+        Random rnd = new Random(System.currentTimeMillis());
+        for (int i = 0; i < 20; i++) {
+            int size = rnd.nextInt(128 * 1024);
+            byte[] bindata = new byte[size];
+            rnd.nextBytes(bindata);
+            String base64String = Base64.encodeBytes(bindata);
+            byte[] bindata2 = Base64.decode(base64String);
+            assertTrue(Arrays.equals(bindata, bindata2));
+        }
+    }
+
+    public void testBase64InputStream() throws IOException {
+        Random rnd = new Random(System.currentTimeMillis());
+        int size = rnd.nextInt(128 * 1024);
+        byte[] bindata = new byte[size];
+        rnd.nextBytes(bindata);
+        File orgFile = File.createTempFile("tmp", "org");
+        File b64File = File.createTempFile("tmp", "b64");
+        File binFile = File.createTempFile("tmp", "bin");
+
+        try {
+            FileOutputStream fout = new FileOutputStream(orgFile);
+            fout.write(bindata);
+            fout.close();
+
+            FileInputStream fin = new FileInputStream(orgFile);
+            fout = new FileOutputStream(b64File);
+            Base64.Base64InputStream b64In = new Base64.Base64InputStream(fin,
+                    Base64.ENCODE);
+            StreamHelper.copyStream(b64In, fout);
+            b64In.close();
+            fout.close();
+
+            fin = new FileInputStream(b64File);
+            fout = new FileOutputStream(binFile);
+            b64In = new Base64.Base64InputStream(fin, Base64.DECODE);
+            StreamHelper.copyStream(b64In, fout);
+            b64In.close();
+            fout.close();
+
+            byte[] bindata2 = new byte[size];
+            fin = new FileInputStream(binFile);
+            fin.read(bindata2);
+            fin.close();
+
+            assertTrue(Arrays.equals(bindata, bindata2));
+        } finally {
+            orgFile.delete();
+            b64File.delete();
+            binFile.delete();
+        }
+    }
+
+    public void testBase64OutputStream() throws IOException {
+        Random rnd = new Random(System.currentTimeMillis());
+        int size = rnd.nextInt(128 * 1024);
+        byte[] bindata = new byte[size];
+        rnd.nextBytes(bindata);
+        File orgFile = File.createTempFile("tmp", "org");
+        File b64File = File.createTempFile("tmp", "b64");
+        File binFile = File.createTempFile("tmp", "bin");
+
+        try {
+            FileOutputStream fout = new FileOutputStream(orgFile);
+            fout.write(bindata);
+            fout.close();
+
+            FileInputStream fin = new FileInputStream(orgFile);
+            fout = new FileOutputStream(b64File);
+            Base64.Base64OutputStream b64Out = new Base64.Base64OutputStream(fout,
+                    Base64.ENCODE);
+            StreamHelper.copyStream(fin, b64Out);
+            b64Out.close();
+            fout.close();
+
+            fin = new FileInputStream(b64File);
+            fout = new FileOutputStream(binFile);
+            b64Out = new Base64.Base64OutputStream(fout, Base64.DECODE);
+            StreamHelper.copyStream(fin, b64Out);
+            b64Out.close();
+            fout.close();
+
+            byte[] bindata2 = new byte[size];
+            fin = new FileInputStream(binFile);
+            fin.read(bindata2);
+            fin.close();
+
+            assertTrue(Arrays.equals(bindata, bindata2));
+        } finally {
+            orgFile.delete();
+            b64File.delete();
+            binFile.delete();
+        }
+    }
+}

+ 157 - 0
src/test/java/de/mcs/utils/event/TestEventFactory.java

@@ -0,0 +1,157 @@
+package de.mcs.utils.event;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestEventFactory {
+
+  private MyEvent1 event1;
+  private MyEvent2 event2;
+  private EventFactory factory;
+  private boolean doEvent1;
+  private boolean doEvent2;
+
+  class MyEvent1 extends AbstractEvent {
+
+    public MyEvent1() {
+      super(MyEvent1.class.getSimpleName());
+    }
+
+    private MyEvent1(String name) {
+      super(name);
+    }
+
+  }
+
+  class MyEvent2 extends AbstractEvent {
+
+    public MyEvent2() {
+      super(MyEvent2.class.getSimpleName());
+    }
+
+    private MyEvent2(String name) {
+      super(name);
+    }
+
+  }
+
+  @Before
+  public void setUp() throws Exception {
+    event1 = new MyEvent1();
+    event2 = new MyEvent2();
+    factory = new EventFactory();
+    factory.registerEvent(event1);
+    factory.registerEvent(event2);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+  }
+
+  @Test
+  public void testRegisterEvent() {
+    Assert.assertTrue("event 1 not registered. ", factory.isEventRegistered(event1));
+    Assert.assertTrue("event 2 not registered. ", factory.isEventRegistered(event2));
+  }
+
+  @Test
+  public void testRegisterListener() {
+    resetEventValues();
+
+    EventListener listener = new EventListener() {
+
+      @Override
+      public void handle(String event, Object... data) {
+        doEvent1 = event.equals(event1.getName());
+        doEvent2 = event.equals(event2.getName());
+      }
+    };
+    factory.registerListener(event1, listener);
+
+    Assert.assertTrue(factory.isListenerRegistered(event1, listener));
+
+    Assert.assertFalse(doEvent1);
+    Assert.assertFalse(doEvent2);
+
+    factory.fireEvent(event1, "nix da");
+
+    Assert.assertTrue(doEvent1);
+    Assert.assertFalse(doEvent2);
+
+    resetEventValues();
+
+    factory.fireEvent(event2, "nix da");
+
+    Assert.assertFalse(doEvent1);
+    Assert.assertFalse(doEvent2);
+
+    resetEventValues();
+
+    factory.unregisterListener(event1, listener);
+
+    factory.fireEvent(event1, "nix da");
+    factory.fireEvent(event2, "nix da");
+
+    Assert.assertFalse(doEvent1);
+    Assert.assertFalse(doEvent2);
+
+    factory.registerListener(event1, listener);
+    factory.registerListener(event2, listener);
+    Assert.assertTrue(factory.isListenerRegistered(event1, listener));
+    Assert.assertTrue(factory.isListenerRegistered(event2, listener));
+
+    resetEventValues();
+
+    Assert.assertFalse(doEvent1);
+    Assert.assertFalse(doEvent2);
+
+    factory.fireEvent(event1, "nix da");
+
+    Assert.assertTrue(doEvent1);
+    Assert.assertFalse(doEvent2);
+
+    factory.fireEvent(event2, "nix da");
+
+    Assert.assertFalse(doEvent1);
+    Assert.assertTrue(doEvent2);
+  }
+
+  @Test
+  public void testAutoDeRegisterListener() {
+    resetEventValues();
+
+    EventListener listener = new EventListener() {
+
+      @Override
+      public void handle(String event, Object... data) {
+        doEvent1 = event.equals(event1.getName());
+        doEvent2 = event.equals(event2.getName());
+      }
+    };
+    factory.registerListener(event1, listener);
+
+    Assert.assertFalse(doEvent1);
+    Assert.assertFalse(doEvent2);
+
+    factory.fireEvent(event1, (Object) null);
+
+    Assert.assertTrue(doEvent1);
+    Assert.assertFalse(doEvent2);
+
+    listener = null;
+    resetEventValues();
+    System.gc();
+
+    factory.fireEvent(event1, (Object) null);
+
+    Assert.assertFalse(doEvent1);
+    Assert.assertFalse(doEvent2);
+  }
+
+  private void resetEventValues() {
+    doEvent1 = false;
+    doEvent2 = false;
+  }
+}

+ 36 - 0
src/test/java/de/mcs/utils/event/TestManagedProperty.java

@@ -0,0 +1,36 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2012 by Wilfried Klaas
+ * Project: MCSUtils
+ * File: TestManagedProperty.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 25.03.2012 Willie
+ */
+
+package de.mcs.utils.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Willie
+ *
+ */
+public class TestManagedProperty {
+
+  /**
+   * Test method for
+   * {@link de.mcs.utils.event.ManagedProperty#ManagedProperty(java.lang.String, de.mcs.utils.event.Delegate, java.lang.Object)}
+   * .
+   */
+  @Test
+  public void testManagedProperty() {
+    Delegate delegate = new DelegateImpl();
+    ManagedProperty<List<String>> managedProperty = new ManagedProperty<List<String>>("list", delegate,
+        new ArrayList<String>());
+    Assert.assertNotNull(managedProperty);
+  }
+}

+ 689 - 0
src/test/java/de/mcs/utils/tstConversions.java

@@ -0,0 +1,689 @@
+/*
+ * MCS Media Computer Software
+ * Copyright (c) 2006 by MCS
+ * --------------------------------------
+ * Created on 06.01.2006 by w.klaas
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package de.mcs.utils;
+
+import junit.framework.TestCase;
+import junitx.framework.ArrayAssert;
+
+public class tstConversions extends TestCase {
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToInt(byte[])'
+     */
+    public void testByteToIntByteArray() throws Exception {
+        byte[] test = new byte[] {0, 0, 0, 0, // value 0
+                127, -1, -1, -1, // value max integer}
+                -128, 0, 0, 0, // value min integer}
+                0, 0, -128, 0, // 32768
+                -1, -1, -128, 0, // -32768
+                0, 0, 0, 1, // 1
+                -1, -1, -1, -1 // -1
+        };
+        int[] testvalues = Conversions.byteToInt(test);
+        assertEquals(test.length >> 2, testvalues.length);
+        assertEquals(0, testvalues[0]);
+        assertEquals(Integer.MAX_VALUE, testvalues[1]);
+        assertEquals(Integer.MIN_VALUE, testvalues[2]);
+        assertEquals(32768, testvalues[3]);
+        assertEquals(-32768, testvalues[4]);
+        assertEquals(1, testvalues[5]);
+        assertEquals(-1, testvalues[6]);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToFloat(byte[])'
+     */
+    public void testByteToFloatByteArray() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToShort(byte[])'
+     */
+    public void testByteToShortByteArray() throws Exception {
+        byte[] test = new byte[] {0, 0, // value 0
+                127, -1, // value max short}
+                -128, 0, // value min short}
+                1, 0, // 256
+                -1, 0, // -256
+                0, -1, // 255
+                -1, 1, // -255
+                0, 1, // 1
+                -1, -1 // -1
+        };
+        short[] testvalues = Conversions.byteToShort(test);
+        assertEquals(test.length >> 1, testvalues.length);
+        assertEquals(0, testvalues[0]);
+        assertEquals(Short.MAX_VALUE, testvalues[1]);
+        assertEquals(Short.MIN_VALUE, testvalues[2]);
+        assertEquals(256, testvalues[3]);
+        assertEquals(-256, testvalues[4]);
+        assertEquals(255, testvalues[5]);
+        assertEquals(-255, testvalues[6]);
+        assertEquals(1, testvalues[7]);
+        assertEquals(-1, testvalues[8]);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToLong(byte[])'
+     */
+    public void testByteToLongByteArray() throws Exception {
+        byte[] test = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, // value 0
+                127, -1, -1, -1, -1, -1, -1, -1, // value max short}
+                -128, 0, 0, 0, 0, 0, 0, 0, // value min short}
+                0, 0, 0, 0, 0, 0, 1, 0, // 256
+                -1, -1, -1, -1, -1, -1, -1, 0, // -256
+                0, 0, 0, 0, 0, 0, 0, -1, // 255
+                -1, -1, -1, -1, -1, -1, -1, 1, // -255
+                0, 0, 0, 0, 0, 0, 0, 1, // 1
+                -1, -1, -1, -1, -1, -1, -1, -1 // -1
+        };
+        long[] testvalues = Conversions.byteToLong(test);
+        assertEquals(test.length >> 3, testvalues.length);
+        assertEquals(0, testvalues[0]);
+        assertEquals(Long.MAX_VALUE, testvalues[1]);
+        assertEquals(Long.MIN_VALUE, testvalues[2]);
+        assertEquals(256, testvalues[3]);
+        assertEquals(-256, testvalues[4]);
+        assertEquals(255, testvalues[5]);
+        assertEquals(-255, testvalues[6]);
+        assertEquals(1, testvalues[7]);
+        assertEquals(-1, testvalues[8]);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToDouble(byte[])'
+     */
+    public void testByteToDoubleByteArray() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToInt(int, int, byte[])'
+     */
+    public void testByteToIntIntIntByteArray() throws Exception {
+        byte[] test = new byte[] {23, 34, 45, // beginning here
+                0, 0, 0, 0, // value 0
+                127, -1, -1, -1, // value max integer}
+                -128, 0, 0, 0, // value min integer}
+                0, 0, -128, 0, // 32768
+                -1, -1, -128, 0, // -32768
+                0, 0, 0, 1, // 1
+                -1, -1, -1, -1, // -1
+                -12, 23, -14, 34, 56, -23 // dump data
+        };
+        int[] testvalues = Conversions.byteToInt(3, 28, test);
+        assertEquals(7, testvalues.length);
+        assertEquals(0, testvalues[0]);
+        assertEquals(Integer.MAX_VALUE, testvalues[1]);
+        assertEquals(Integer.MIN_VALUE, testvalues[2]);
+        assertEquals(32768, testvalues[3]);
+        assertEquals(-32768, testvalues[4]);
+        assertEquals(1, testvalues[5]);
+        assertEquals(-1, testvalues[6]);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToInt(byte[], int)'
+     */
+    public void testByteToIntByteArrayInt() throws Exception {
+        byte[] test = new byte[] {23, 34, 45, // beginning here
+                0, 0, 0, 0, // value 0
+                127, -1, -1, -1, // value max integer}
+                -128, 0, 0, 0, // value min integer}
+                0, 0, -128, 0, // 32768
+                -1, -1, -128, 0, // -32768
+                0, 0, 0, 1, // 1
+                -1, -1, -1, -1 // -1
+        };
+        int testvalue = Conversions.byteToInt(test, 3);
+        assertEquals(0, testvalue);
+        testvalue = Conversions.byteToInt(test, 7);
+        assertEquals(Integer.MAX_VALUE, testvalue);
+        testvalue = Conversions.byteToInt(test, 11);
+        assertEquals(Integer.MIN_VALUE, testvalue);
+        testvalue = Conversions.byteToInt(test, 15);
+        assertEquals(32768, testvalue);
+        testvalue = Conversions.byteToInt(test, 19);
+        assertEquals(-32768, testvalue);
+        testvalue = Conversions.byteToInt(test, 23);
+        assertEquals(1, testvalue);
+        testvalue = Conversions.byteToInt(test, 27);
+        assertEquals(-1, testvalue);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToShort(int, int, byte[])'
+     */
+    public void testByteToShortIntIntByteArray() throws Exception {
+        byte[] test = new byte[] {12, 34, -123, // beginning here
+                0, 0, // value 0
+                127, -1, // value max short}
+                -128, 0, // value min short}
+                1, 0, // 256
+                -1, 0, // -256
+                0, -1, // 255
+                -1, 1, // -255
+                0, 1, // 1
+                -1, -1, // -1
+                -12, 23, -14, 34, 56, -23 // dump data
+        };
+        short[] testvalues = Conversions.byteToShort(3, 18, test);
+        assertEquals(9, testvalues.length);
+        assertEquals(0, testvalues[0]);
+        assertEquals(Short.MAX_VALUE, testvalues[1]);
+        assertEquals(Short.MIN_VALUE, testvalues[2]);
+        assertEquals(256, testvalues[3]);
+        assertEquals(-256, testvalues[4]);
+        assertEquals(255, testvalues[5]);
+        assertEquals(-255, testvalues[6]);
+        assertEquals(1, testvalues[7]);
+        assertEquals(-1, testvalues[8]);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToShort(byte[], int)'
+     */
+    public void testByteToShortByteArrayInt() throws Exception {
+        byte[] test = new byte[] {12, 34, -123, // beginning here
+                0, 0, // value 0
+                127, -1, // value max short}
+                -128, 0, // value min short}
+                1, 0, // 256
+                -1, 0, // -256
+                0, -1, // 255
+                -1, 1, // -255
+                0, 1, // 1
+                -1, -1 // -1
+        };
+        short testvalues = Conversions.byteToShort(test, 3);
+        assertEquals(0, testvalues);
+        testvalues = Conversions.byteToShort(test, 5);
+        assertEquals(Short.MAX_VALUE, testvalues);
+        testvalues = Conversions.byteToShort(test, 7);
+        assertEquals(Short.MIN_VALUE, testvalues);
+        testvalues = Conversions.byteToShort(test, 9);
+        assertEquals(256, testvalues);
+        testvalues = Conversions.byteToShort(test, 11);
+        assertEquals(-256, testvalues);
+        testvalues = Conversions.byteToShort(test, 13);
+        assertEquals(255, testvalues);
+        testvalues = Conversions.byteToShort(test, 15);
+        assertEquals(-255, testvalues);
+        testvalues = Conversions.byteToShort(test, 17);
+        assertEquals(1, testvalues);
+        testvalues = Conversions.byteToShort(test, 19);
+        assertEquals(-1, testvalues);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToFloat(int, int, byte[])'
+     */
+    public void testByteToFloatIntIntByteArray() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToFloat(byte[], int)'
+     */
+    public void testByteToFloatByteArrayInt() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToLong(int, int, byte[])'
+     */
+    public void testByteToLongIntIntByteArray() throws Exception {
+        byte[] test = new byte[] {12, 34, -123, // beginning here
+                0, 0, 0, 0, 0, 0, 0, 0, // value 0
+                127, -1, -1, -1, -1, -1, -1, -1, // value max short}
+                -128, 0, 0, 0, 0, 0, 0, 0, // value min short}
+                0, 0, 0, 0, 0, 0, 1, 0, // 256
+                -1, -1, -1, -1, -1, -1, -1, 0, // -256
+                0, 0, 0, 0, 0, 0, 0, -1, // 255
+                -1, -1, -1, -1, -1, -1, -1, 1, // -255
+                0, 0, 0, 0, 0, 0, 0, 1, // 1
+                -1, -1, -1, -1, -1, -1, -1, -1, // -1
+                -12, 23, -14, 34, 56, -23 // dump data
+        };
+        long[] testvalues = Conversions.byteToLong(3, 72, test);
+        assertEquals(9, testvalues.length);
+        assertEquals(0, testvalues[0]);
+        assertEquals(Long.MAX_VALUE, testvalues[1]);
+        assertEquals(Long.MIN_VALUE, testvalues[2]);
+        assertEquals(256, testvalues[3]);
+        assertEquals(-256, testvalues[4]);
+        assertEquals(255, testvalues[5]);
+        assertEquals(-255, testvalues[6]);
+        assertEquals(1, testvalues[7]);
+        assertEquals(-1, testvalues[8]);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToLong(byte[], int)'
+     */
+    public void testByteToLongByteArrayInt() throws Exception {
+        byte[] test = new byte[] {12, 34, -123, // beginning here
+                0, 0, 0, 0, 0, 0, 0, 0, // value 0
+                127, -1, -1, -1, -1, -1, -1, -1, // value max short}
+                -128, 0, 0, 0, 0, 0, 0, 0, // value min short}
+                0, 0, 0, 0, 0, 0, 1, 0, // 256
+                -1, -1, -1, -1, -1, -1, -1, 0, // -256
+                0, 0, 0, 0, 0, 0, 0, -1, // 255
+                -1, -1, -1, -1, -1, -1, -1, 1, // -255
+                0, 0, 0, 0, 0, 0, 0, 1, // 1
+                -1, -1, -1, -1, -1, -1, -1, -1, // -1
+                -12, 23, -14, 34, 56, -23 // dump data
+        };
+        long testvalue = Conversions.byteToLong(test, 3);
+        assertEquals(0, testvalue);
+        testvalue = Conversions.byteToLong(test, 11);
+        assertEquals(Long.MAX_VALUE, testvalue);
+        testvalue = Conversions.byteToLong(test, 19);
+        assertEquals(Long.MIN_VALUE, testvalue);
+        testvalue = Conversions.byteToLong(test, 27);
+        assertEquals(256, testvalue);
+        testvalue = Conversions.byteToLong(test, 35);
+        assertEquals(-256, testvalue);
+        testvalue = Conversions.byteToLong(test, 43);
+        assertEquals(255, testvalue);
+        testvalue = Conversions.byteToLong(test, 51);
+        assertEquals(-255, testvalue);
+        testvalue = Conversions.byteToLong(test, 59);
+        assertEquals(1, testvalue);
+        testvalue = Conversions.byteToLong(test, 67);
+        assertEquals(-1, testvalue);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToDouble(int, int, byte[])'
+     */
+    public void testByteToDoubleIntIntByteArray() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToDouble(byte[], int)'
+     */
+    public void testByteToDoubleByteArrayInt() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.intToByte(int, int, int[])'
+     */
+    public void testIntToByteIntIntIntArray() throws Exception {
+        int[] testvalues = new int[] {23, 45, 0, Integer.MAX_VALUE,
+                Integer.MIN_VALUE, 32768, -32768, 1, -1 };
+        byte[] test = new byte[] {0, 0, 0, 0, // value 0
+                127, -1, -1, -1, // value max integer}
+                -128, 0, 0, 0, // value min integer}
+                0, 0, -128, 0, // 32768
+                -1, -1, -128, 0, // -32768
+                0, 0, 0, 1, // 1
+                -1, -1, -1, -1 // -1
+        };
+        byte[] values = Conversions.intToByte(2, 7, testvalues);
+        ArrayAssert.assertEquals(test, values);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.shortToByte(int, int, short[])'
+     */
+    public void testShortToByteIntIntShortArray() throws Exception {
+        short[] testvalues = new short[] {23, 45, 0, Short.MAX_VALUE,
+                Short.MIN_VALUE, 256, -256, 255, -255, 1, -1 };
+        byte[] test = new byte[] {0, 0, // value 0
+                127, -1, // value max short}
+                -128, 0, // value min short}
+                1, 0, // 256
+                -1, 0, // -256
+                0, -1, // 255
+                -1, 1, // -255
+                0, 1, // 1
+                -1, -1 // -1
+        };
+        byte[] values = Conversions.shortToByte(2, 9, testvalues);
+        ArrayAssert.assertEquals(test, values);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.floatToByte(int, int, float[])'
+     */
+    public void testFloatToByteIntIntFloatArray() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.longToByte(int, int, long[])'
+     */
+    public void testLongToByteIntIntLongArray() throws Exception {
+        long[] testvalues = new long[] {23, 45, 0, Long.MAX_VALUE,
+                Long.MIN_VALUE, 256, -256, 255, -255, 1, -1 };
+        byte[] test = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, // value 0
+                127, -1, -1, -1, -1, -1, -1, -1, // value max short}
+                -128, 0, 0, 0, 0, 0, 0, 0, // value min short}
+                0, 0, 0, 0, 0, 0, 1, 0, // 256
+                -1, -1, -1, -1, -1, -1, -1, 0, // -256
+                0, 0, 0, 0, 0, 0, 0, -1, // 255
+                -1, -1, -1, -1, -1, -1, -1, 1, // -255
+                0, 0, 0, 0, 0, 0, 0, 1, // 1
+                -1, -1, -1, -1, -1, -1, -1, -1 // -1
+        };
+        byte[] values = Conversions.longToByte(2, 9, testvalues);
+        ArrayAssert.assertEquals(test, values);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.doubleToByte(int, int,
+     * double[])'
+     */
+    public void testDoubleToByteIntIntDoubleArray() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToByte(byte)'
+     */
+    public void testByteToByteByte() {
+        for (int i = -128; i < 128; i++) {
+            byte[] value = Conversions.byteToByte((byte) i);
+            assertEquals(1, value.length);
+            assertEquals((byte) i, value[0]);
+        }
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToByte(Byte)'
+     */
+    public void testByteToByteByte1() {
+        for (int i = -128; i < 128; i++) {
+            byte[] value = Conversions.byteToByte(new Byte((byte) i));
+            assertEquals(1, value.length);
+            assertEquals((byte) i, value[0]);
+        }
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.intToByte(int)'
+     */
+    public void testIntToByteInt() {
+        byte[] values = Conversions.intToByte(0);
+        byte[] test = new byte[] {0, 0, 0, 0 }; // value 0
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(Integer.MAX_VALUE);
+        test = new byte[] {127, -1, -1, -1 }; // value max integer}
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(Integer.MIN_VALUE);
+        test = new byte[] {-128, 0, 0, 0 }; // value min integer}
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(32768);
+        test = new byte[] {0, 0, -128, 0 }; // 32768
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(-32768);
+        test = new byte[] {-1, -1, -128, 0 }; // -32768
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(1);
+        test = new byte[] {0, 0, 0, 1 }; // 1
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(-1);
+        test = new byte[] {-1, -1, -1, -1 }; // -1
+        ArrayAssert.assertEquals(test, values);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.intToByte(Integer)'
+     */
+    public void testIntToByteInteger() {
+        byte[] values = Conversions.intToByte(new Integer(0));
+        byte[] test = new byte[] {0, 0, 0, 0 }; // value 0
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(new Integer(Integer.MAX_VALUE));
+        test = new byte[] {127, -1, -1, -1 }; // value max integer}
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(new Integer(Integer.MIN_VALUE));
+        test = new byte[] {-128, 0, 0, 0 }; // value min integer}
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(new Integer(32768));
+        test = new byte[] {0, 0, -128, 0 }; // 32768
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(new Integer(-32768));
+        test = new byte[] {-1, -1, -128, 0 }; // -32768
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(new Integer(1));
+        test = new byte[] {0, 0, 0, 1 }; // 1
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.intToByte(new Integer(-1));
+        test = new byte[] {-1, -1, -1, -1 }; // -1
+        ArrayAssert.assertEquals(test, values);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.shortToByte(short)'
+     */
+    public void testShortToByteShort() {
+        byte[] values = Conversions.shortToByte((short) 0);
+        byte[] test = new byte[] {0, 0 }; // value 0
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte((short) Short.MAX_VALUE);
+        test = new byte[] {127, -1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte((short) Short.MIN_VALUE);
+        test = new byte[] {-128, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte((short) 256);
+        test = new byte[] {1, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte((short) -256);
+        test = new byte[] {-1, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte((short) 255);
+        test = new byte[] {0, -1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte((short) -255);
+        test = new byte[] {-1, 1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte((short) 1);
+        test = new byte[] {0, 1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte((short) -1);
+        test = new byte[] {-1, -1 };
+        ArrayAssert.assertEquals(test, values);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.shortToByte(Short)'
+     */
+    public void testShortToByteShort1() {
+        byte[] values = Conversions.shortToByte(new Short((short) 0));
+        byte[] test = new byte[] {0, 0 }; // value 0
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte(new Short(Short.MAX_VALUE));
+        test = new byte[] {127, -1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte(new Short(Short.MIN_VALUE));
+        test = new byte[] {-128, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte(new Short((short) 256));
+        test = new byte[] {1, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte(new Short((short) -256));
+        test = new byte[] {-1, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte(new Short((short) 255));
+        test = new byte[] {0, -1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte(new Short((short) -255));
+        test = new byte[] {-1, 1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte(new Short((short) 1));
+        test = new byte[] {0, 1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.shortToByte(new Short((short) -1));
+        test = new byte[] {-1, -1 };
+        ArrayAssert.assertEquals(test, values);
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.floatToByte(float)'
+     */
+    public void testFloatToByteFloat() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.floatToByte(Float)'
+     */
+    public void testFloatToByteFloat1() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.longToByte(long)'
+     */
+    public void testLongToByteLong() {
+        byte[] values = Conversions.longToByte((long) 0);
+        byte[] test = new byte[] {0, 0, 0, 0, 0, 0, 0, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte((long) Long.MAX_VALUE);
+        test = new byte[] {127, -1, -1, -1, -1, -1, -1, -1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte((long) Long.MIN_VALUE);
+        test = new byte[] {-128, 0, 0, 0, 0, 0, 0, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte((long) 256);
+        test = new byte[] {0, 0, 0, 0, 0, 0, 1, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte((long) -256);
+        test = new byte[] {-1, -1, -1, -1, -1, -1, -1, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte((long) 255);
+        test = new byte[] {0, 0, 0, 0, 0, 0, 0, -1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte((long) -255);
+        test = new byte[] {-1, -1, -1, -1, -1, -1, -1, 1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte((long) 1);
+        test = new byte[] {0, 0, 0, 0, 0, 0, 0, 1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte((long) -1);
+        test = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1 };
+        ArrayAssert.assertEquals(test, values);
+
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.longToByte(Long)'
+     */
+    public void testLongToByteLong1() {
+        byte[] values = Conversions.longToByte(new Long((long) 0));
+        byte[] test = new byte[] {0, 0, 0, 0, 0, 0, 0, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte(new Long((long) Long.MAX_VALUE));
+        test = new byte[] {127, -1, -1, -1, -1, -1, -1, -1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte(new Long((long) Long.MIN_VALUE));
+        test = new byte[] {-128, 0, 0, 0, 0, 0, 0, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte(new Long((long) 256));
+        test = new byte[] {0, 0, 0, 0, 0, 0, 1, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte(new Long((long) -256));
+        test = new byte[] {-1, -1, -1, -1, -1, -1, -1, 0 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte(new Long((long) 255));
+        test = new byte[] {0, 0, 0, 0, 0, 0, 0, -1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte(new Long((long) -255));
+        test = new byte[] {-1, -1, -1, -1, -1, -1, -1, 1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte(new Long((long) 1));
+        test = new byte[] {0, 0, 0, 0, 0, 0, 0, 1 };
+        ArrayAssert.assertEquals(test, values);
+
+        values = Conversions.longToByte(new Long((long) -1));
+        test = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1 };
+        ArrayAssert.assertEquals(test, values);
+
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.doubleToByte(double)'
+     */
+    public void testDoubleToByteDouble() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.doubleToByte(Double)'
+     */
+    public void testDoubleToByteDouble1() {
+        // TODO implement this
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.Conversions.byteToNumber(byte[], Object)'
+     */
+    public void testByteToNumber() {
+        // TODO implement this
+    }
+}

+ 66 - 0
src/test/java/de/mcs/utils/tstWorkingThread.java

@@ -0,0 +1,66 @@
+/*
+ * MCS Media Computer Software
+ * Copyright (c) 2006 by MCS
+ * --------------------------------------
+ * Created on 11.04.2006 by w.klaas
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package de.mcs.utils;
+
+import junit.framework.TestCase;
+
+public class tstWorkingThread extends TestCase {
+
+    class MyRunnable implements Runnable {
+        boolean stop = false;
+
+        public void run() {
+            while (!stop) {
+                try {
+                    Thread.sleep(10);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+
+        }
+    }
+
+    /*
+     * Test method for 'de.mcs.utils.WorkingThread.run()'
+     */
+    public void testRun() throws InterruptedException {
+        WorkingThread thread = new WorkingThread();
+        assertTrue(thread.isFree());
+        thread.start();
+        Thread.sleep(100);
+        assertTrue(thread.isFree());
+        assertFalse(thread.isAlive());
+        thread.setBusy();
+        assertFalse(thread.isFree());
+        thread.setFree();
+        assertTrue(thread.isFree());
+
+        thread = new WorkingThread();
+        MyRunnable runnable = new MyRunnable();
+        thread.setRunnable(runnable);
+        assertTrue(thread.isFree());
+        thread.start();
+        Thread.sleep(100);
+        assertFalse(thread.isFree());
+        runnable.stop = true;
+        Thread.sleep(100);
+        assertTrue(thread.isFree());
+    }
+}