diff --git a/.gitignore b/.gitignore
index d490c49a5f87527ee513e67e14609613580bb17c..1042105475ae0e18e9fe3b7d2363043c67844644 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,11 @@
-.build
-dist
-.tmp
-nbproject
-review
+*~
+\#*
+.\#*
+TAGS
+ChangeLog
 vendor
-\#*#
-.#*#
-TAGS
\ No newline at end of file
+review
+build
+composer.phar
+composer.lock
+test.php
diff --git a/.hgignore b/.hgignore
deleted file mode 100644
index 7c31106ff27301d688bd31c94a2361c12b72193f..0000000000000000000000000000000000000000
--- a/.hgignore
+++ /dev/null
@@ -1,11 +0,0 @@
-syntax: glob
-
-.build
-.dist
-nbproject
-review
-tmp
-vendor
-\#*#
-.#*#
-TAGS
\ No newline at end of file
diff --git a/build.local.xml b/build.local.xml
deleted file mode 100644
index 90aa3bc92e323653b9ef5c828f07c987d84dd494..0000000000000000000000000000000000000000
--- a/build.local.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<project name="local" default="help">
-  <target name="help">
-    <echo message="This component has no local build targets." />
-  </target>
-</project>
-<!-- vim: set tabstop=2 shiftwidth=2 expandtab: -->
diff --git a/build.properties b/build.properties
deleted file mode 100644
index bc00f56fc2dec8164e479618d23d7ec0a4b96799..0000000000000000000000000000000000000000
--- a/build.properties
+++ /dev/null
@@ -1,9 +0,0 @@
-project.name=PicaReader
-project.channel=hab20.hab.de/service/pear
-project.majorVersion=0
-project.minorVersion=1
-project.patchLevel=0
-project.snapshot=true
-
-component.type=php-library
-component.version=11
diff --git a/build.xml b/build.xml
deleted file mode 100644
index 058dd58861b5dbcf39e35c8170f845cc1663b1b7..0000000000000000000000000000000000000000
--- a/build.xml
+++ /dev/null
@@ -1,500 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- build file for phing -->
-<project default="help" basedir=".">
-  <!-- Human-readable info about our component -->
-  <property file="build.properties" />
-  <taskdef name="now" classname="Phix_Project.ComponentManager.Phing.NowTask" />
-  <now name="date.now"/>
-  <if>
-    <and>
-      <isset property="project.snapshot"/>
-      <istrue value="${project.snapshot}"/>
-    </and>
-    <then>
-      <property name="project.version" value="${project.majorVersion}.${project.minorVersion}.${project.patchLevel}snapshot${date.now}" />
-      <property name="project.stability" value="snapshot" />
-    </then>
-    <else>
-      <property name="project.version" value="${project.majorVersion}.${project.minorVersion}.${project.patchLevel}" />
-      <property name="project.stability" value="stable" />
-    </else>
-  </if>
-  <property name="project.apiversion"      value="${project.majorVersion}.${project.minorVersion}" />
-
-  <!-- Paths to the directories that we work with -->
-  <property name="project.srcdir"          value="${project.basedir}/src" override="true" />
-  <property name="project.src.phpdir"      value="${project.srcdir}/php" override="true" />
-  <property name="project.src.bindir"      value="${project.srcdir}/bin" override="true" />
-  <property name="project.src.datadir"     value="${project.srcdir}/data" override="true" />
-  <property name="project.src.docdir"      value="${project.srcdir}/docs" override="true" />
-  <property name="project.src.testdir"     value="${project.srcdir}/tests" override="true" />
-  <property name="project.src.wwwdir"      value="${project.srcdir}/www" override="true" />
-  <property name="project.src.testunitdir" value="${project.src.testdir}/unit-tests" override="true" />
-  <property name="project.src.testintdir"  value="${project.src.testdir}/integration-tests" override="true" />
-  <property name="project.src.testfuncdir" value="${project.src.testdir}/functional-tests" override="true" />
-
-  <property name="project.reviewdir"       value="${project.basedir}/review" override="true" />
-  <property name="project.review.logsdir"  value="${project.basedir}/review/logs" override="true" />
-  <property name="project.review.docsdir"  value="${project.reviewdir}/docs" override="true" />
-  <property name="project.review.codebrowserdir" value="${project.reviewdir}/code-browser" override="true" />
-  <property name="project.review.codecoveragedir" value="${project.reviewdir}/code-coverage" override="true" />
-
-  <property name="project.builddir"        value="${project.basedir}/.build"  override="true" />
-  <property name="project.pkgdir"          value="${project.builddir}/${project.name}-${project.version}" override="true" />
-  <property name="project.tmpdir"          value="${project.basedir}/.tmp" override="true" />
-
-  <property name="project.vendordir"       value="${project.basedir}/vendor" override="true" />
-  <property name="project.vendor.bindir"   value="${project.vendordir}/bin" override="true" />
-  <property name="project.vendor.datadir"  value="${project.vendordir}/data" override="true" />
-  <property name="project.vendor.phpdir"   value="${project.vendordir}/php" override="true" />
-  <property name="project.vendor.testdir"  value="${project.vendordir}/tests" override="true" />
-  <property name="project.vendor.docdir"   value="${project.vendordir}/docs" override="true" />
-  <property name="project.vendor.wwwdir"   value="${project.vendordir}/www" override="true" />
-
-  <property name="project.distdir"         value="${project.basedir}/dist" />
-  <property name="project.distdir.lastBuilt" value="${project.basedir}/dist/lastBuilt" />
-  <property name="project.tarfilename"     value="${project.name}-${project.version}.tgz" />
-  <property name="project.tarfile"         value="${project.distdir}/${project.tarfilename}" />
-
-  <!-- what was the last PEAR package we created, if any? -->
-  <if>
-    <available file="${project.distdir.lastBuilt}"/>
-    <then>
-      <property file="${project.distdir.lastBuilt}"/>
-    </then>
-    <else>
-      <property name="project.lastBuiltTarfile" value="false"/>
-    </else>
-  </if>
-
-  <!-- override this if you want to run additional PEAR commands -->
-  <property name="pear.cmd" value="" override="true" />
-
-  <!-- lists of the files that make up our package -->
-  <fileset dir="${project.src.bindir}" id="binfiles">
-    <include name="**/**"/>
-    <exclude name="**/.DS_Store"/>
-    <exclude name="**/.empty"/>
-    <exclude name="**/.svn"/>
-  </fileset>
-  <fileset dir="${project.src.datadir}" id="datafiles">
-    <include name="**/**"/>
-    <exclude name="**/.DS_Store"/>
-    <exclude name="**/.empty"/>
-    <exclude name="**/.svn"/>
-  </fileset>
-  <fileset dir="${project.src.phpdir}" id="phpfiles">
-    <include name="**/**"/>
-    <exclude name="**/.DS_Store"/>
-    <exclude name="**/.empty"/>
-    <exclude name="**/.svn"/>
-  </fileset>
-  <fileset dir="${project.src.testunitdir}/php" id="testfiles">
-    <include name="**/**"/>
-    <exclude name="**/.DS_Store"/>
-    <exclude name="**/.empty"/>
-    <exclude name="**/.svn"/>
-  </fileset>
-  <fileset dir="${project.src.wwwdir}" id="wwwfiles">
-    <include name="**/**" />
-    <exclude name="**/.DS_Store"/>
-    <exclude name="**/.empty"/>
-    <exclude name="**/.svn"/>
-  </fileset>
-  <fileset dir="${project.src.docdir}" id="docfiles">
-    <include name="**/**" />
-    <exclude name="**/.DS_Store"/>
-    <exclude name="**/.empty"/>
-    <exclude name="**/.svn"/>
-  </fileset>
-  <fileset dir="${project.basedir}" id="topleveldocfiles">
-    <include name="*.txt" />
-    <include name="*.md" />
-  </fileset>
-
-  <taskdef name="phingcallifexists" classname="Phix_Project.ComponentManager.Phing.PhingCallIfExistsTask" />
-  <import file="build.local.xml"/>
-
-  <!-- Tell the user what this build file supports -->
-  <target name="help">
-    <echo message="${project.name} ${project.version}: build.xml targets:" />
-    <echo message="" />
-    <echo message="Setup your dev environment:" />
-    <echo message="" />
-    <echo message="  build-vendor" />
-    <echo message="    Populate vendor/ with this package's dependencies" />
-    <echo message="  vendor-pear" />
-    <echo message="    Run additional PEAR commands inside the vendor folder" />
-    <echo message="" />
-    <echo message="Develop your code:" />
-    <echo message="" />
-    <echo message="  lint" />
-    <echo message="    Check the PHP files for syntax errors" />
-    <echo message="  test" />
-    <echo message="    Run the component's PHPUnit tests" />
-    <echo message="  code-review" />
-    <echo message="    Run all of the code quality targets:" />
-    <echo message="" />
-    <echo message="    code-browser" />
-    <echo message="      Run code quality tests for PHP_CodeBrowser" />
-    <echo message="    phpcpd" />
-    <echo message="      Check for cut and paste problems" />
-    <echo message="    phploc" />
-    <echo message="      Calculate the size of your PHP project" />
-    <echo message="    phpdoc" />
-    <echo message="      Create the PHP docs from source code" />
-    <echo message="" />
-    <echo message="Publish your component:" />
-    <echo message="" />
-    <echo message="  pear-package" />
-    <echo message="    Create a PEAR-compatible package" />
-    <echo message="  publish-local" />
-    <echo message="    Publish your PEAR-compatible package into a local copy" />
-    <echo message="    of your PEAR channel" />
-    <echo message="  install-vendor" />
-    <echo message="    Install this component from source into vendor/" />
-    <echo message="  install-system" />
-    <echo message="    Install this component from source for all local users" />
-    <echo message="    You must be root to run this target on Linux!!" />
-    <echo message=""/>
-    <echo message="Maintain your component:"/>
-    <echo message=""/>
-    <echo message="  upgrade-skeleton"/>
-    <echo message="    Upgrade the skeleton files for this component"/>
-    <echo message=""/>
-    <echo message="Additional targets:" />
-    <echo message=""/>
-    <echo message="  clean" />
-    <echo message="    Remove all temporary folders created by this build file" />
-    <echo message="  version" />
-    <echo message="    Show this component's version from build.properties" />
-    <echo message="" />
-    <phingcallifexists target="local.help" />
-  </target>
-
-  <!-- Show the current version, as set in build.properties -->
-  <!-- This is just to be a time-saver -->
-  <target name="version">
-    <echo message="${project.version}" />
-  </target>
-
-  <!-- Run PHP lint on all of the source code -->
-  <target name="lint">
-    <phplint>
-      <fileset dir="${project.src.phpdir}">
-        <include name="**/*.php" />
-      </fileset>
-    </phplint>
-    <phingcallifexists target="local.lint" />
-  </target>
-
-  <!-- Run the unit tests for this module -->
-  <target name="run-unittests" depends="lint">
-    <!-- Make sure vendor/ folder exists -->
-    <if>
-      <not>
-        <available file="${project.vendordir}" type="dir"/>
-      </not>
-      <then>
-        <phingcall target="build-vendor"/>
-      </then>
-    </if>
-
-    <!-- do we have any tests? -->
-    <!-- currently cannot think of a reliable way to test this in phing -->
-
-    <!-- run the tests -->
-    <delete dir="${project.review.codecoveragedir}" />
-    <mkdir dir="${project.review.codecoveragedir}" />
-    <mkdir dir="${project.review.logsdir}" />
-    <exec command="phpunit" checkreturn="true" logoutput="true"/>
-    <echo/>
-    <echo>The code coverage report is in file://${project.review.codecoveragedir}</echo>
-    <echo/>
-  </target>
-
-  <!-- Run all the tests for this module -->
-  <target name="test" depends="run-unittests">
-    <phingcallifexists target="local.test"/>
-  </target>
-
-  <!-- Run the code review quality tests -->
-  <target name="code-review" depends="run-unittests, code-browser, phpcpd, pdepend, phploc">
-    <phingcallifexists target="local.code-review"/>
-  </target>
-
-  <!-- Run all of the targets for setting up the code browser -->
-  <target name="code-browser" depends="phpmd, phpcs, phpcb">
-    <phingcallifexists target="local.code-browser"/>
-  </target>
-
-  <target name="pdepend">
-    <mkdir dir="${project.review.logsdir}" />
-    <exec command="pdepend --phpunit-xml=${project.review.logsdir}/pdepend.xml --jdepend-xml=${project.review.logsdir}/jdepend.xml --jdepend-chart=${project.review.logsdir}/dependencies.svg --overview-pyramid=${project.review.logsdir}/overview-pyramid.svg ${project.src.phpdir}" logoutput="true"/>
-  </target>
-
-  <!-- Generate package docs -->
-  <target name="phpdoc">
-    <mkdir dir="${project.review.logsdir}" />
-    <exec command="phpdoc -d ${project.src.phpdir} -t ${project.review.docsdir}" logoutput="true"/>
-    <echo message="You will find the PHPDoc for your project at: ${project.review.docsdir}/index.html"/>
-    <phingcallifexists target="local.phpdoc"/>
-  </target>
-
-  <!-- Check code for code smells -->
-  <target name="phpmd">
-    <mkdir dir="${project.review.logsdir}" />
-    <exec command="phpmd ${project.src.phpdir} xml codesize,design,naming,unusedcode --reportfile ${project.review.logsdir}/phpmd.xml" logoutput="true" />
-  </target>
-
-  <target name="phpcpd">
-    <mkdir dir="${project.review.logsdir}"/>
-    <exec command="phpcpd --log-pmd ${project.review.logsdir}/pmd-cpd.xml ${project.src.phpdir}" logoutput="true" />
-  </target>
-
-  <!-- Check the code for style violations -->
-  <target name="phpcs">
-    <mkdir dir="${project.review.logsdir}" />
-    <exec command="phpcs --report=xml --report-file=${project.review.logsdir}/checkstyle.xml --standard=${checkstyle.standard} --extensions=php ${project.src.phpdir}" logoutput="true"/>
-  </target>
-
-  <!-- Build the code-browser files -->
-  <target name="phpcb" depends="phpmd">
-    <delete dir="${project.review.codebrowserdir}" />
-    <mkdir dir="${project.review.codebrowserdir}" />
-    <exec command="phpcb --log ${project.review.logsdir} --source ${project.src.phpdir} --output ${project.review.codebrowserdir}" logoutput="true" />
-  </target>
-
-  <!-- Work out the size of the project -->
-  <target name="phploc">
-    <mkdir dir="${project.review.logsdir}" />
-    <exec command="phploc --log-xml ${project.review.logsdir}/phploc.xml --log-csv ${project.review.logsdir}/phploc.csv ${project.src.phpdir}" logoutput="true" />
-  </target>
-
-  <!-- Populate vendor with the dependencies for this component -->
-  <target name="build-vendor" depends="pear-package,setup-vendor">
-    <echo>Populating vendor/ with dependencies</echo>
-    <exec command="phix pear:register-channels" checkreturn="true" logoutput="true" />
-    <exec command="pear -c ${project.tmpdir}/pear-config install --alldeps ${project.tarfile}" logoutput="true" checkreturn="true"/>
-    <echo/>
-    <echo>Your vendor/ folder has been built.</echo>
-    <echo>You only need to run 'phing build-vendor' again if you change the</echo>
-    <echo>dependencies listed in your package.xml file.</echo>
-    <echo/>
-    <phingcallifexists target="local.buildvendor"/>
-  </target>
-
-  <!-- Setup the vendor folder -->
-  <target name="setup-vendor">
-    <echo>Creating vendor/ as a sandboxed PEAR install folder</echo>
-    <delete dir="${project.vendordir}" />
-    <mkdir dir="${project.vendordir}" />
-    <delete dir="${project.tmpdir}" />
-    <mkdir dir="${project.tmpdir}" />
-    <exec command="pear config-create ${project.tmpdir} ${project.tmpdir}/pear-config" checkreturn="true" logoutput="true" />
-    <exec command="pear -c ${project.tmpdir}/pear-config config-set preferred_state alpha" checkreturn="true" logoutput="true" />
-    <exec command="pear -c ${project.tmpdir}/pear-config config-set php_dir ${project.vendor.phpdir}" checkreturn="true" logoutput="true" />
-    <exec command="pear -c ${project.tmpdir}/pear-config config-set bin_dir ${project.vendor.bindir}" checkreturn="true" logoutput="true" />
-    <exec command="pear -c ${project.tmpdir}/pear-config config-set data_dir ${project.vendor.datadir}" checkreturn="true" logoutput="true" />
-    <exec command="pear -c ${project.tmpdir}/pear-config config-set doc_dir ${project.vendor.docdir}" checkreturn="true" logoutput="true" />
-    <exec command="pear -c ${project.tmpdir}/pear-config config-set test_dir ${project.vendor.testdir}" checkreturn="true" logoutput="true" />
-    <exec command="pear -c ${project.tmpdir}/pear-config config-set www_dir ${project.vendor.wwwdir}" checkreturn="true" logoutput="true" />
-  </target>
-
-  <!-- Create the PEAR package, ready for release -->
-  <target name="pear-package">
-    <echo>Building release directory</echo>
-    <delete dir="${project.builddir}" />
-    <mkdir dir="${project.pkgdir}" />
-    <if>
-      <available file="${project.src.bindir}"/>
-      <then>
-        <copy todir="${project.pkgdir}">
-          <fileset refid="binfiles"/>
-        </copy>
-      </then>
-    </if>
-    <if>
-      <available file="${project.src.datadir}"/>
-      <then>
-        <copy todir="${project.pkgdir}">
-          <fileset refid="datafiles"/>
-        </copy>
-      </then>
-    </if>
-    <if>
-      <available file="${project.src.docdir}"/>
-      <then>
-        <copy todir="${project.pkgdir}">
-          <fileset refid="docfiles"/>
-        </copy>
-      </then>
-    </if>
-    <if>
-      <available file="${project.src.phpdir}"/>
-      <then>
-        <copy todir="${project.pkgdir}">
-          <fileset refid="phpfiles"/>
-        </copy>
-      </then>
-    </if>
-    <if>
-      <available file="${project.src.testunitdir}"/>
-      <then>
-        <copy todir="${project.pkgdir}">
-          <fileset refid="testfiles"/>
-        </copy>
-      </then>
-    </if>
-    <if>
-      <available file="${project.src.wwwdir}"/>
-      <then>
-        <copy todir="${project.pkgdir}">
-          <fileset refid="wwwfiles"/>
-        </copy>
-      </then>
-    </if>
-    <copy todir="${project.pkgdir}">
-      <fileset refid="topleveldocfiles"/>
-    </copy>
-    <copy todir="${project.builddir}">
-      <fileset dir=".">
-        <include name="package.xml" />
-      </fileset>
-    </copy>
-
-    <exec command="phix pear:expand-package-xml" checkreturn="yes" logoutput="yes"/>
-
-    <echo>Creating ${project.tarfile} PEAR package</echo>
-
-    <mkdir dir="${project.distdir}" />
-    <delete file="${project.tarfile}" />
-    <tar destfile="${project.tarfile}" compression="gzip">
-      <fileset dir="${project.builddir}">
-        <include name="**/**" />
-      </fileset>
-    </tar>
-
-    <!-- remember the tarball we have just build -->
-    <property name="project.lastBuiltTarfile" value="${project.tarfile}" override="true"/>
-    <echo file="${project.distdir.lastBuilt}" append="false">project.lastBuiltTarfile=${project.tarfile}</echo>
-
-    <!-- write a message to say which file we built last -->
-    <echo>Your PEAR package is in ${project.tarfile}</echo>
-    <phingcallifexists target="local.pear-package"/>
-  </target>
-
-  <!-- Install the code -->
-  <target name="install-vendor">
-    <if>
-      <not>
-        <contains string="${project.lastBuiltTarfile}" substring="${project.name}"/>
-      </not>
-      <then>
-        <fail message="Please run 'phing pear-package' first, then try again."/>
-      </then>
-    </if>
-
-    <if>
-      <not>
-        <available file="${project.vendordir"/>
-      </not>
-      <then>
-        <phingcall target="build-vendor" />
-      </then>
-    </if>
-
-    <if>
-      <available file="${project.lastBuiltTarfile}"/>
-      <then>
-        <exec command="pear -c ${project.tmpdir}/pear-config install --alldeps -f ${project.lastBuiltTarfile}" logoutput="true" checkreturn="true"/>
-        <phingcallifexists target="local.install-vendor"/>
-      </then>
-      <else>
-        <echo>Cannot find PEAR package file ${project.lastBuiltTarfile}</echo>
-        <fail message="Run 'phing pear-package' to create a new PEAR package, then try again."/>
-      </else>
-    </if>
-  </target>
-
-  <!-- install a package system-wide -->
-  <target name="install-system">
-    <if>
-      <not>
-        <contains string="${project.lastBuiltTarfile}" substring="${project.name}"/>
-      </not>
-      <then>
-        <echo>Please run 'phing pear-package' first, then try again.</echo>
-      </then>
-      <elseif>
-        <available file="${project.lastBuiltTarfile}"/>
-        <then>
-          <exec command="pear install -f -a ${project.lastBuiltTarfile}" checkreturn="true" logoutput="true" />
-          <phingcallifexists target="local.install-system"/>
-        </then>
-      </elseif>
-      <else>
-        <echo>Cannot find PEAR package file ${project.lastBuiltTarfile}</echo>
-        <echo>Run 'phing pear-package' to create a new PEAR package, then try again</echo>
-      </else>
-    </if>
-  </target>
-
-  <!-- Publish to local copy of PEAR channel -->
-  <target name="publish-local" depends="pear-package">
-    <if>
-      <not>
-        <contains string="${project.lastBuiltTarfile}" substring="${project.name}"/>
-      </not>
-      <then>
-        <echo>Please run 'phing pear-package' first, then try again.</echo>
-      </then>
-      <elseif>
-        <available file="${project.lastBuiltTarfile}"/>
-        <then>
-          <!-- get rid of any existing snapshots we may have published -->
-          <foreach param="packagefile" absparam="abspackagefile" target="pirum-remove-package">
-            <fileset dir="${pear.local}/get">
-              <include name="${project.name}*snapshot*.tgz" />
-            </fileset>
-          </foreach>
-
-          <!-- publish the new PEAR package -->
-          <exec command="pirum add ${pear.local} ${project.lastBuiltTarfile}" checkreturn="true" logoutput="true" />
-          <phingcallifexists target="local.publish-local"/>
-        </then>
-      </elseif>
-      <else>
-        <echo>Cannot find PEAR package file ${project.lastBuiltTarfile}</echo>
-        <echo>Run 'phing pear-package' to create a new PEAR package, then try again</echo>
-      </else>
-    </if>
-  </target>
-
-  <target name="pirum-remove-package">
-    <exec command="pirum remove ${pear.local} ${packagefile}" logoutput="true" checkreturn="true" />
-  </target>
-
-  <!-- Run additional PEAR commands in the vendor folder -->
-  <target name="vendor-pear">
-    <exec command="pear -c ${project.tmpdir}/pear-config ${pear.cmd}" logoutput="true" checkreturn="true" />
-  </target>
-
-  <!-- Upgrade the skeleton files here and now -->
-  <target name="upgrade-skeleton">
-    <exec command="phix ${component.type}:upgrade ." logoutput="true" checkreturn="true" />
-    <phingcallifexists target="local.upgrade-skeleton"/>
-  </target>
-
-  <!-- Clean up the mess -->
-  <target name="clean">
-    <delete dir="${project.builddir}" />
-    <delete dir="${project.distdir}" />
-    <delete dir="${project.reviewdir}" />
-    <delete dir="${project.pkgdir}" />
-    <delete dir="${project.distdir}" />
-    <delete dir="${project.tmpdir}" />
-    <phingcallifexists target="local.clean"/>
-  </target>
-</project>
-<!-- vim: set tabstop=2 shiftwidth=2 expandtab: -->
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..84dced52e3ada89c375417b15db460db87a9875e
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,27 @@
+{
+    "name": "hab/picareader",
+    "description": "Classes for reading Pica+ records encoded in Pica, PicaXML and PicaPlain",
+    "type": "library",
+    "license": "GPL-3.0+",
+    "authors": [
+	{
+	    "name": "David Maus",
+	    "email": "maus@hab.de",
+	    "role": "Developer"
+	}
+    ],
+    "support": {
+	"email": "maus@hab.de"
+    },
+    "repositories": [
+        { "type": "vcs", "url": "http://github.com/dmj/PicaRecord"}
+    ],
+    "require": {
+        "hab/picarecord": "dev-next"
+    },
+    "autoload": {
+        "psr-0": {
+            "HAB\\Pica": "src/"
+        }
+    }
+}
diff --git a/package.xml b/package.xml
deleted file mode 100644
index 2f2ab8d4479538cd0bfe3a8f52c2886080928895..0000000000000000000000000000000000000000
--- a/package.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<package packagerversion="1.9.1" version="2.0" 
-	 xmlns="http://pear.php.net/dtd/package-2.0" 
-	 xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" 
-	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
-	 xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
-			     http://pear.php.net/dtd/tasks-1.0.xsd
-			     http://pear.php.net/dtd/package-2.0
-			     http://pear.php.net/dtd/package-2.0.xsd">
-  <name>${project.name}</name>
-  <channel>${project.channel}</channel>
-  <summary>Classes for reading Pica+ records</summary>
-  <description>
-    This package provides classes for reading Pica+ records encoded in
-    PicaXML or PicaPlain.
-  </description>
-  <lead>
-    <name>David Maus</name>
-    <user>dmaus</user>
-    <email>maus@hab.de</email>
-    <active>yes</active>
-  </lead>
-  <date>${build.date}</date>
-  <time>${build.time}</time>
-  <version>
-    <release>${project.version}</release>
-    <api>${project.majorVersion}.${project.minorVersion}</api>
-  </version>
-  <stability>
-    <release>${project.stability}</release>
-    <api>stable</api>
-  </stability>
-  <license>GNU General Public License v3</license>
-  <notes>
-    No notes.
-  </notes>
-  <contents>
-    <dir baseinstalldir="/" name="/">
-      ${contents}
-    </dir>
-  </contents>
-  <dependencies>
-    <required>
-      <php>
-        <min>5.3.0</min>
-      </php>
-      <pearinstaller>
-        <min>1.9.4</min>
-      </pearinstaller>
-      <package>
-        <name>Autoloader</name>
-        <channel>pear.phix-project.org</channel>
-        <min>3.0.0</min>
-        <max>3.999.9999</max>
-      </package>
-      <package>
-        <name>PicaRecord</name>
-        <channel>hab20.hab.de/service/pear</channel>
-        <min>0.1.0</min>
-        <max>0.999.9999</max>
-      </package>
-    </required>
-  </dependencies>
-  <phprelease />
-  <changelog>
-    <release>
-      <version>
-        <release>0.1.0</release>
-        <api>0.1</api>
-      </version>
-      <stability>
-        <release>stable</release>
-        <api>stable</api>
-      </stability>
-      <date>2012-02-15</date>
-      <license>GNU General Public License v3</license>
-      <notes>
-      </notes>
-    </release>
-  </changelog>
-</package>
-<!-- vim: set tabstop=2 shiftwidth=2 expandtab: -->
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..16d058a388443b5bc7d54508aa1c8321d6314561
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<phpunit bootstrap="tests/bootstrap.php" strict="true">
+  <testsuites>
+    <testsuite name="Unit Tests">
+      <directory suffix="Test.php">tests</directory>
+    </testsuite>
+  </testsuites>
+  <filter>
+    <blacklist>
+      <directory suffix=".php">vendor</directory>
+      <directory suffix=".php">tests</directory>
+    </blacklist>
+    <whitelist addUncoveredFilesFromWhitelist="true">
+      <directory suffix=".php">bin</directory>
+      <directory suffix=".php">src</directory>
+    </whitelist>
+  </filter>
+  <logging>
+    <log type="coverage-html" target="review/code-coverage"/>
+  </logging>
+</phpunit>
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
deleted file mode 100644
index 864f7f7890b1ac293fb5de9ae2bf1c90824915d5..0000000000000000000000000000000000000000
--- a/phpunit.xml.dist
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0"?>
-<phpunit bootstrap="src/tests/unit-tests/bootstrap.php">
-    <testsuites>
-        <testsuite name="Unit Tests">
-            <directory suffix="Test.php">src/tests/unit-tests</directory>
-        </testsuite>
-    </testsuites>
-    <filter>
-        <blacklist>
-            <directory suffix=".php">vendor</directory>
-            <directory suffix=".php">src/tests</directory>
-        </blacklist>
-        <whitelist addUncoveredFilesFromWhitelist="true">
-            <directory suffix=".php">src/bin</directory>
-            <directory suffix=".php">src/php</directory>
-        </whitelist>
-    </filter>
-    <logging>
-        <log type="coverage-html" target="review/code-coverage"/>
-        <log type="coverage-clover" target="review/logs/phpunit.xml"/>
-        <log type="json" target="review/logs/phpunit.json"/>
-        <log type="tap" target="review/logs/phpunit.tap"/>
-        <log type="junit" target="review/logs/phpunit-junit.xml"/>
-        <log type="testdox-html" target="review/testdox.html"/>
-        <log type="testdox-text" target="review/testdox.txt"/>
-    </logging>
-</phpunit>
-<!-- vim: set tabstop=4 shiftwidth=4 expandtab: -->
diff --git a/src/php/HAB/Pica/Parser/PicaPlainParser.php b/src/HAB/Pica/Parser/PicaPlainParser.php
similarity index 80%
rename from src/php/HAB/Pica/Parser/PicaPlainParser.php
rename to src/HAB/Pica/Parser/PicaPlainParser.php
index 4cfbe3cdf0ecf89a4f3bc4aa53710589e3432a35..c35ce275af7011652b78c7074414fd50d57bdf93 100644
--- a/src/php/HAB/Pica/Parser/PicaPlainParser.php
+++ b/src/HAB/Pica/Parser/PicaPlainParser.php
@@ -20,31 +20,27 @@
  *
  * @package   PicaReader
  * @author    David Maus <maus@hab.de>
- * @copyright Copyright (c) 2012 by Herzog August Bibliothek Wolfenbüttel
+ * @copyright Copyright (c) 2012, 2013 by Herzog August Bibliothek Wolfenbüttel
  * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
  */
 
 namespace HAB\Pica\Parser;
 
-/**
- * Parser for Pica+ records encoded in PicaPlain.
- *
- * @package   PicaReader
- * @author    David Maus <maus@hab.de>
- * @copyright Copyright (c) 2012 by Herzog August Bibliothek Wolfenbüttel
- * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
- */
+use RuntimeException;
+
 class PicaPlainParser
 {
 
     /**
      * Return array representation of the field encoded in a line.
      *
-     * @throws \RuntimeException Invalid characters in line
+     * @throws RuntimeException Invalid characters in line
+     *
      * @param  string $line PicaPlain record line
-     * @return array Array representation of the encoded field
+     * @return array
      */
-    public static function parseField ($line) {
+    public static function parseField ($line) 
+    {
         $field = array('subfields' => array());
         $match = array();
         if (preg_match('#^([012][0-9]{2}[A-Z@])(/([0-9]{2}))? (\$.*)$#Du', $line, $match)) {
@@ -52,7 +48,7 @@ class PicaPlainParser
                            'occurrence' => $match[3] ?: null,
                            'subfields' => self::parseSubfields($match[4]));;
         } else {
-            throw new \RuntimeException("Invalid characters in PicaPlain record at line: {$line}");
+            throw new RuntimeException("Invalid characters in PicaPlain record at line: {$line}");
         }
         return $field;
     }
@@ -61,9 +57,10 @@ class PicaPlainParser
      * Return array of array representations of the subfields encode in argument.
      *
      * @param  string $str Encoded subfields
-     * @return array Array representions of the encoded subfields
+     * @return array
      */
-    public static function parseSubfields ($str) {
+    public static function parseSubfields ($str) 
+    {
         $subfields = array();
         $subfield = null;
         $pos = 0;
diff --git a/src/HAB/Pica/Reader/PicaPlainReader.php b/src/HAB/Pica/Reader/PicaPlainReader.php
new file mode 100644
index 0000000000000000000000000000000000000000..0d5c0c0552b70038de0f5e9b22493a284755b3b3
--- /dev/null
+++ b/src/HAB/Pica/Reader/PicaPlainReader.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * The PicaPlainReader class file.
+ *
+ * This file is part of PicaReader.
+ *
+ * PicaReader 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.
+ *
+ * PicaReader 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 PicaReader.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   PicaReader
+ * @author    David Maus <maus@hab.de>
+ * @copyright Copyright (c) 2012, 2013 by Herzog August Bibliothek Wolfenbüttel
+ * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
+ */
+
+namespace HAB\Pica\Reader;
+
+use HAB\Pica\Parser\PicaPlainParser;
+
+class PicaPlainReader extends Reader
+{
+
+    /**
+     * Current input data.
+     *
+     * @var string
+     */
+    protected $_data;
+
+    /**
+     * Open the reader with input data.
+     *
+     * @param  string $data Input data
+     * @return void
+     */
+    public function open ($data)
+    {
+        parent::open($data);
+        $this->_data = preg_split("/(?:\n\r|[\n\r])/", $data);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function next ()
+    {
+        $record = false;
+        if (current($this->_data) !== false) {
+            $record = array('fields' => array());
+            do {
+                $line = current($this->_data);
+                $record['fields'] []= PicaPlainParser::parseField($line);
+            } while (next($this->_data));
+            next($this->_data);
+        }
+        return $record;
+    }
+
+    /**
+     * Close the reader.
+     *
+     * @return void
+     */
+    public function close ()
+    {
+        parent::close();
+        $this->_data = null;
+    }
+}
diff --git a/src/HAB/Pica/Reader/PicaXmlReader.php b/src/HAB/Pica/Reader/PicaXmlReader.php
new file mode 100644
index 0000000000000000000000000000000000000000..7baa0cf020875ba7af904b0b484cdf3fb394645c
--- /dev/null
+++ b/src/HAB/Pica/Reader/PicaXmlReader.php
@@ -0,0 +1,173 @@
+<?php
+
+/**
+ * The PicaXmlReader class file.
+ *
+ * This file is part of PicaReader.
+ *
+ * PicaReader 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.
+ *
+ * PicaReader 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 PicaReader.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   PicaReader
+ * @author    David Maus <maus@hab.de>
+ * @copyright Copyright (c) 2012 by Herzog August Bibliothek Wolfenbüttel
+ * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
+ */
+
+namespace HAB\Pica\Reader;
+
+use XMLReader;
+
+class PicaXmlReader extends Reader
+{
+
+    /**
+     * @var string XML namespace URI of PicaXML
+     */
+    const PICAXML_NAMESPACE_URI = 'info:srw/schema/5/picaXML-v1.0';
+
+    /**
+     * @var XMLReader XML Reader instance
+     */
+    private $_xmlReader;
+
+    /**
+     * Constructor.
+     *
+     * @return void
+     */
+    public function __construct ()
+    {
+        parent::__construct();
+        $this->_xmlReader = new XMLReader();
+    }
+
+    /**
+     * Prepare the reader for reading data.
+     *
+     * @param  string|resource $input
+     * @return void
+     */
+    public function open ($input)
+    {
+        if (is_resource($input)) {
+            $input = stream_get_contents($input);
+        }
+        $this->_xmlReader->XML($input);
+        parent::open($input);
+    }
+
+    /**
+     * Close current input data.
+     *
+     * @return void
+     */
+    public function close ()
+    {
+        $this->_xmlReader->close();
+        parent::close();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function next ()
+    {
+        if ($this->forwardTo('record', self::PICAXML_NAMESPACE_URI)) {
+            $record = array('fields' => array());
+            while (!$this->atElementEnd('record', self::PICAXML_NAMESPACE_URI) && $this->_xmlReader->read()) {
+                if ($this->atElement('datafield', self::PICAXML_NAMESPACE_URI)) {
+                    $record['fields'] []= $this->readField();
+                }
+            }
+        } else {
+            $record = false;
+        }
+        return $record;
+    }
+
+    /**
+     * Return array representation of datafield at cursor.
+     *
+     * The cursor is expected to be positioned on the opening field element.
+     *
+     * @return array
+     */
+    protected function readField ()
+    {
+        $field = array('tag' => $this->_xmlReader->getAttribute('tag'),
+                       'occurrence' => $this->_xmlReader->getAttribute('occurrence'),
+                       'subfields' => array());
+        while (!$this->atElementEnd('datafield', self::PICAXML_NAMESPACE_URI) && $this->_xmlReader->read()) {
+            if ($this->atElement('subfield', self::PICAXML_NAMESPACE_URI)) {
+                $subfield = array('code' => $this->_xmlReader->getAttribute('code'), 'value' => '');
+                while (!$this->atElementEnd('subfield', self::PICAXML_NAMESPACE_URI) && $this->_xmlReader->read()) {
+                    switch ($this->_xmlReader->nodeType) {
+                        case XMLReader::TEXT:
+                        case XMLReader::SIGNIFICANT_WHITESPACE:
+                        case XMLReader::CDATA:
+                            $subfield['value'] .= $this->_xmlReader->value;
+                            break;
+                    }
+                }
+                $field['subfields'] []= $subfield;
+            }
+        }
+        return $field;
+    }
+
+    /**
+     * Move cursor forward to named element.
+     *
+     * The cursor is not moved if it is already positioned at the named
+     * element. Returns true if cursor is at specified element and false if
+     * cursor reached the end of the document
+     *
+     * @param  string $name Element local name
+     * @param  string $uri  Namespace URI
+     * @return boolean
+     */
+    protected function forwardTo ($name, $uri)
+    {
+        while (!$this->atElement($name, $uri) && $this->_xmlReader->read()) { }
+        return ($this->_xmlReader->nodeType === XMLReader::ELEMENT);
+    }
+
+    /**
+     * Return true if the cursor is positioned at the named element.
+     *
+     * @param  string $name Element local name
+     * @param  string $uri  Namespace URI
+     * @return boolean
+     */
+    protected function atElement ($name, $uri)
+    {
+        return ($this->_xmlReader->nodeType === XMLReader::ELEMENT &&
+                $this->_xmlReader->localName === $name &&
+                $this->_xmlReader->namespaceURI === $uri);
+    }
+
+    /**
+     * Return TRUE if the cursor is positioned at the end of the named element.
+     *
+     * @param  string $name Element local name
+     * @param  string $uri  Namespace URI
+     * @return boolean
+     */
+    protected function atElementEnd ($name, $uri)
+    {
+        return ($this->_xmlReader->nodeType === XMLReader::END_ELEMENT &&
+                $this->_xmlReader->localName === $name &&
+                $this->_xmlReader->namespaceURI === $uri);
+    }
+}
diff --git a/src/HAB/Pica/Reader/Reader.php b/src/HAB/Pica/Reader/Reader.php
new file mode 100644
index 0000000000000000000000000000000000000000..3484eca67244b196bd88d956787892d5eb752057
--- /dev/null
+++ b/src/HAB/Pica/Reader/Reader.php
@@ -0,0 +1,184 @@
+<?php
+
+/**
+ * The Reader class file.
+ *
+ * This file is part of PicaReader.
+ *
+ * PicaReader 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.
+ *
+ * PicaReader 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 PicaReader.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   PicaReader
+ * @author    David Maus <maus@hab.de>
+ * @copyright Copyright (c) 2012, 2013 by Herzog August Bibliothek Wolfenbüttel
+ * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
+ */
+
+namespace HAB\Pica\Reader;
+
+use Exception;
+use RuntimeException;
+
+use HAB\Pica\Record\Record;
+
+abstract class Reader
+{
+
+    /**
+     * TRUE if the reader was opened with input data.
+     *
+     * @var boolean
+     */
+    protected $_isOpen = false;
+
+    /**
+     * Filter function or NULL if none set.
+     *
+     * @see Reader::setFilter()
+     *
+     * @var callback|null
+     */
+    protected $_filter = null;
+
+    /**
+     * Constructor.
+     *
+     * @return void
+     */
+    public function __construct ()
+    {}
+
+    /**
+     * Open the reader with input data.
+     *
+     * @return void
+     */
+    public function open ($data)
+    {
+        if ($this->isOpen()) {
+            $this->close();
+        }
+        $this->_isOpen = true;
+    }
+
+    /**
+     * Return next record in input data or FALSE if no more records.
+     *
+     * This function uses the Record::factory() method to create a record and
+     * applies a possible filter function to the input data.
+     *
+     * @see Reader::setFilter()
+     * @see Record::factory()
+     *
+     * @throws RuntimeException Error creating a record instance via factory function
+     * @return Record|false
+     */
+    public function read ()
+    {
+        $record = $this->next();
+        if (is_array($record)) {
+            $record = $this->applyFilter($record);
+            if (is_array($record)) {
+                try {
+                    return Record::factory($record);
+                } catch (Exception $e) {
+                    throw new RuntimeException("Error creating record instance in Record::factory()", -1, $e);
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Set a filter function.
+     *
+     * A filter function is every valid callback function that takes the array
+     * representation of a record as only argument and returns a possibly
+     * modifed array or false to skip the record.
+     *
+     * @param  callback $filter Filter function
+     * @return array|false
+     */
+    public function setFilter ($filter)
+    {
+        $this->_filter = $filter;
+    }
+
+    /**
+     * Return current filter function.
+     *
+     * @return callback|null
+     */
+    public function getFilter ()
+    {
+        return $this->_filter;
+    }
+
+    /**
+     * Unset the filter function.
+     *
+     * @return void
+     */
+    public function unsetFilter ()
+    {
+        $this->_filter = null;
+    }
+
+    /**
+     * Close the reader.
+     *
+     * @return void
+     */
+    public function close ()
+    {
+        $this->_isOpen = false;
+    }
+
+    /**
+     * Return true if the reader is open.
+     *
+     * @return boolean
+     */
+    public function isOpen ()
+    {
+        return $this->_isOpen;
+    }
+
+
+    /**
+     * Read the next record in input data.
+     *
+     * Returns array representation of the record or false if no more records.
+     *
+     * @return array|false
+     */
+    abstract protected function next ();
+
+    /**
+     * Return filtered record.
+     *
+     * Applies the filter function to the array representation of a record.
+     *
+     * @param  array $record Array representation of record
+     * @return array|false
+     */
+    protected function applyFilter (array $record)
+    {
+        $filter = $this->getFilter();
+        if ($filter) {
+            return call_user_func($filter, $record);
+        } else {
+            return $record;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/README.txt b/src/README.txt
deleted file mode 100644
index 4074f38738f3847625a1a7e521e392a80fde11cd..0000000000000000000000000000000000000000
--- a/src/README.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-Your src/ folder
-================
-
-This src/ folder is where you put all of your code for release.  There's
-a folder for each type of file that the PEAR Installer supports.  You can
-find out more about these file types online at:
-
-http://blog.stuartherbert.com/php/2011/04/04/explaining-file-roles/
-
-  * bin/
-
-    If you're creating any command-line tools, this is where you'd put
-    them.  Files in here get installed into /usr/bin on Linux et al.
-
-    There is more information available here: http://blog.stuartherbert.com/php/2011/04/06/php-components-shipping-a-command-line-program/
-
-    You can find an example here: https://github.com/stuartherbert/phix/tree/master/src/bin
-
-  * data/
-
-    If you have any data files (any files that aren't PHP code, and which
-    don't belong in the www/ folder), this is the folder to put them in.
-
-    There is more information available here: http://blog.stuartherbert.com/php/2011/04/11/php-components-shipping-data-files-with-your-components/
-
-    You can find an example here: https://github.com/stuartherbert/ComponentManagerPhpLibrary/tree/master/src/data
-
-  * php/
-
-    This is where your component's PHP code belongs.  Everything that goes
-    into this folder must be PSR0-compliant, so that it works with the
-    supplied autoloader.
-
-    There is more information available here: http://blog.stuartherbert.com/php/2011/04/05/php-components-shipping-reusable-php-code/
-
-    You can find an example here: https://github.com/stuartherbert/ContractLib/tree/master/src/php
-
-  * tests/functional-tests/
-
-    Right now, this folder is just a placeholder for future functionality.
-    You're welcome to make use of it yourself.
-
-  * tests/integration-tests/
-
-    Right now, this folder is just a placeholder for future functionality.
-    You're welcome to make use of it yourself.
-
-  * tests/unit-tests/
-
-    This is where all of your PHPUnit tests go.
-
-    It needs to contain _exactly_ the same folder structure as the src/php/
-    folder.  For each of your PHP classes in src/php/, there should be a
-    corresponding test file in test/unit-tests.
-
-    There is more information available here: http://blog.stuartherbert.com/php/2011/08/15/php-components-shipping-unit-tests-with-your-component/
-
-    You can find an example here: https://github.com/stuartherbert/ContractLib/tree/master/test/unit-tests
-
-  * www/
-
-    This folder is for any files that should be published in a web server's
-    DocRoot folder.
-
-    It's quite unusual for components to put anything in this folder, but
-    it is there just in case.
-
-    There is more information available here: http://blog.stuartherbert.com/php/2011/08/16/php-components-shipping-web-pages-with-your-components/
diff --git a/src/bin/.empty b/src/bin/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/data/.empty b/src/data/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/docs/.empty b/src/docs/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/php/.empty b/src/php/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/php/HAB/Pica/Reader/PicaPlainReader.php b/src/php/HAB/Pica/Reader/PicaPlainReader.php
deleted file mode 100644
index 5b1c76b3b73cfbf36d1db4407e9f9a644aefef9a..0000000000000000000000000000000000000000
--- a/src/php/HAB/Pica/Reader/PicaPlainReader.php
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-
-/**
- * The PicaPlainReader class file.
- *
- * This file is part of PicaReader.
- *
- * PicaReader 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.
- *
- * PicaReader 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 PicaReader.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   PicaReader
- * @author    David Maus <maus@hab.de>
- * @copyright Copyright (c) 2012 by Herzog August Bibliothek Wolfenbüttel
- * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
- */
-
-namespace HAB\Pica\Reader;
-
-use HAB\Pica\Parser\PicaPlainParser;
-
-/**
- * Reader for Pica+ records encoded in PicaPlain.
- *
- * @package   PicaReader
- * @author    David Maus <maus@hab.de>
- * @copyright Copyright (c) 2012 by Herzog August Bibliothek Wolfenbüttel
- * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
- */
-class PicaPlainReader extends Reader {
-
-  /**
-   * Current input data.
-   *
-   * @var string
-   */
-  protected $_data;
-
-  /**
-   * Open the reader with input data.
-   *
-   * @param  string $data Input data
-   * @return void
-   */
-  public function open ($data) {
-    parent::open($data);
-    $this->_data = preg_split("/(?:\n\r|[\n\r])/", $data);
-  }
-
-  /**
-   * Read the next record in input data.
-   *
-   * @see \HAB\Pica\Reader\Reader::next()
-   *
-   * @return array|false Array representation of the record or FALSE if no more records
-   */
-  protected function next () {
-    $record = false;
-    if (current($this->_data) !== false) {
-      $record = array('fields' => array());
-      do {
-        $line = current($this->_data);
-        $record['fields'] []= PicaPlainParser::parseField($line);
-      } while (next($this->_data));
-      next($this->_data);
-    }
-    return $record;
-  }
-
-  /**
-   * Close the reader.
-   *
-   * @return void
-   */
-  public function close () {
-    parent::close();
-    $this->_data = null;
-  }
-
-  /**
-   * Return the number of the line currently parsed.
-   *
-   * @return integer Number of currently parsed line
-   */
-  protected function getCurrentLineNumber () {
-    return key($this->_data);
-  }
-}
diff --git a/src/php/HAB/Pica/Reader/PicaXmlReader.php b/src/php/HAB/Pica/Reader/PicaXmlReader.php
deleted file mode 100644
index 6fda04c7cf3df951ee0ccdeacf7b12c96074a728..0000000000000000000000000000000000000000
--- a/src/php/HAB/Pica/Reader/PicaXmlReader.php
+++ /dev/null
@@ -1,173 +0,0 @@
-<?php
-
-/**
- * The PicaXmlReader class file.
- *
- * This file is part of PicaReader.
- *
- * PicaReader 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.
- *
- * PicaReader 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 PicaReader.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   PicaReader
- * @author    David Maus <maus@hab.de>
- * @copyright Copyright (c) 2012 by Herzog August Bibliothek Wolfenbüttel
- * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
- */
-
-namespace HAB\Pica\Reader;
-
-use \XMLReader;
-
-/**
- * Reader for Pica+ records encoded in PicaXML.
- *
- * @package   PicaReader
- * @author    David Maus <maus@hab.de>
- * @copyright Copyright (c) 2012 by Herzog August Bibliothek Wolfenbüttel
- * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
- */
-class PicaXmlReader extends Reader {
-
-  /**
-   * @var string XML namespace URI of PicaXML
-   */
-  const PICAXML_NAMESPACE_URI = 'info:srw/schema/5/picaXML-v1.0';
-
-  /**
-   * @var \XMLReader XML Reader instance
-   */
-  private $_xmlReader;
-
-  /**
-   * Constructor.
-   *
-   * @return void
-   */
-  public function __construct () {
-    parent::__construct();
-    $this->_xmlReader = new XMLReader();
-  }
-
-  /**
-   * Prepare the reader for reading data.
-   *
-   * @param  string $input Data to read
-   * @return void
-   */
-  public function open ($input) {
-    $this->_xmlReader->XML($input);
-    parent::open($input);
-  }
-
-  /**
-   * Close current input data.
-   *
-   * @return void
-   */
-  public function close () {
-    $this->_xmlReader->close();
-    parent::close();
-  }
-
-  /**
-   * Return the array representation of the next record in current input data.
-   *
-   * @see    ReaderAbstract::next()
-   *
-   * @return array|false Record or false if there are no more records
-   */
-  protected function next () {
-    if ($this->forwardTo('record', self::PICAXML_NAMESPACE_URI)) {
-      $record = array('fields' => array());
-      while (!$this->atElementEnd('record', self::PICAXML_NAMESPACE_URI) && $this->_xmlReader->read()) {
-        if ($this->atElement('datafield', self::PICAXML_NAMESPACE_URI)) {
-          $record['fields'] []= $this->readField();
-        }
-      }
-    } else {
-      $record = false;
-    }
-    return $record;
-  }
-
-  /**
-   * Return array representation of datafield at cursor.
-   *
-   * The cursor is expected to be positioned on the opening field element.
-   *
-   * @return array Field
-   */
-  protected function readField () {
-    $field = array('tag' => $this->_xmlReader->getAttribute('tag'),
-                   'occurrence' => $this->_xmlReader->getAttribute('occurrence'),
-                   'subfields' => array());
-    while (!$this->atElementEnd('datafield', self::PICAXML_NAMESPACE_URI) && $this->_xmlReader->read()) {
-      if ($this->atElement('subfield', self::PICAXML_NAMESPACE_URI)) {
-        $subfield = array('code' => $this->_xmlReader->getAttribute('code'), 'value' => '');
-        while (!$this->atElementEnd('subfield', self::PICAXML_NAMESPACE_URI) && $this->_xmlReader->read()) {
-          switch ($this->_xmlReader->nodeType) {
-            case XMLReader::TEXT:
-            case XMLReader::SIGNIFICANT_WHITESPACE:
-            case XMLReader::CDATA:
-              $subfield['value'] .= $this->_xmlReader->value;
-              break;
-          }
-        }
-        $field['subfields'] []= $subfield;
-      }
-    }
-    return $field;
-  }
-
-  /**
-   * Move cursor forward to named element.
-   *
-   * The cursor is not moved if it is already positioned at the named element.
-   *
-   * @param  string $name Element local name
-   * @param  string $uri Namespace URI
-   * @return boolean TRUE if cursor is at specified element or FALSE if cursor
-   *         reached the end of the document
-   */
-  protected function forwardTo ($name, $uri) {
-    while (!$this->atElement($name, $uri) && $this->_xmlReader->read()) { }
-    return ($this->_xmlReader->nodeType === XMLReader::ELEMENT);
-  }
-
-  /**
-   * Return TRUE if the cursor is positioned at the named element.
-   *
-   * @param  string $name Element local name
-   * @param  string $uri Namespace URI
-   * @return boolean TRUE if the cursor is positioned at the element
-   */
-  protected function atElement ($name, $uri) {
-    return ($this->_xmlReader->nodeType === XMLReader::ELEMENT &&
-            $this->_xmlReader->localName === $name &&
-            $this->_xmlReader->namespaceURI === $uri);
-  }
-
-  /**
-   * Return TRUE if the cursor is positioned at the end of the named element.
-   *
-   * @param  string $name Element local name
-   * @param  string $uri Namespace URI
-   * @return boolean TRUE if the cursor is positioned at the end of the named
-   *         element
-   */
-  protected function atElementEnd ($name, $uri) {
-    return ($this->_xmlReader->nodeType === XMLReader::END_ELEMENT &&
-            $this->_xmlReader->localName === $name &&
-            $this->_xmlReader->namespaceURI === $uri);
-  }
-}
diff --git a/src/php/HAB/Pica/Reader/Reader.php b/src/php/HAB/Pica/Reader/Reader.php
deleted file mode 100644
index 0d2aa0675b8e9eb8f915027a9982fd395972b702..0000000000000000000000000000000000000000
--- a/src/php/HAB/Pica/Reader/Reader.php
+++ /dev/null
@@ -1,176 +0,0 @@
-<?php
-
-/**
- * The Reader class file.
- *
- * This file is part of PicaReader.
- *
- * PicaReader 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.
- *
- * PicaReader 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 PicaReader.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   PicaReader
- * @author    David Maus <maus@hab.de>
- * @copyright Copyright (c) 2012 by Herzog August Bibliothek Wolfenbüttel
- * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
- */
-
-namespace HAB\Pica\Reader;
-
-/**
- * Abstract base class of Pica+ readers.
- *
- * @package   PicaReader
- * @author    David Maus <maus@hab.de>
- * @copyright Copyright (c) 2012 by Herzog August Bibliothek Wolfenbüttel
- * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
- */
-abstract class Reader {
-
-  /**
-   * TRUE if the reader was opened with input data.
-   *
-   * @var boolean
-   */
-  protected $_isOpen = false;
-
-  /**
-   * Filter function or NULL if none set.
-   *
-   * @see Reader::setFilter()
-   *
-   * @var callback|null
-   */
-  protected $_filter = null;
-
-  /**
-   * Constructor.
-   *
-   * @return void
-   */
-  public function __construct () {
-  }
-
-  /**
-   * Open the reader with input data.
-   *
-   * @param  string $data Input data
-   * @return void
-   */
-  public function open ($data) {
-    if ($this->isOpen()) {
-      $this->close();
-    }
-    $this->_isOpen = true;
-  }
-
-  /**
-   * Return next record in input data or FALSE if no more records.
-   *
-   * This function uses the \HAB\Pica\Record\Record::factory() method to create a record and
-   * applies a possible filter function to the input data.
-   *
-   * @see \HAB\Pica\Reader\Reader::setFilter()
-   * @see \HAB\Pica\Record\Record::factory()
-   *
-   * @throws \RuntimeException Error creating a record instance via factory function
-   * @return \HAB\Pica\Record\Record|FALSE Next record in input data
-   */
-  public function read () {
-    $record = $this->next();
-    if (is_array($record)) {
-      $record = $this->applyFilter($record);
-      if (is_array($record)) {
-        try {
-          return \HAB\Pica\Record\Record::factory($record);
-        } catch (\Exception $e) {
-          throw new \RuntimeException("Error creating record instance in Record::factory()", -1, $e);
-        }
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Read the next record in input data.
-   *
-   * @return array|FALSE Array representation of the record or FALSE if no more records
-   */
-  abstract protected function next ();
-
-  /**
-   * Return filtered record.
-   *
-   * Applies the filter function to the array representation of a record.
-   *
-   * @param  array $record Array representation of record
-   * @return array|false Filtered record or FALSE to skip the record
-   */
-  protected function applyFilter (array $record) {
-    $filter = $this->getFilter();
-    if ($filter) {
-      return call_user_func($filter, $record);
-    } else {
-      return $record;
-    }
-  }
-
-  /**
-   * Set a filter function.
-   *
-   * A filter function is every valid callback function that takes the array
-   * representation of a record as only argument and returns a possibly
-   * modifed array or FALSE to skip the record.
-   *
-   * @param  callback $filter Filter function
-   * @return array|FALSE Filtered record or FALSE if the record should be skipped
-   */
-  public function setFilter ($filter) {
-    $this->_filter = $filter;
-  }
-
-  /**
-   * Return current filter function.
-   *
-   * @return callback|NULL Current filter function or NULL if not set
-   */
-  public function getFilter () {
-    return $this->_filter;
-  }
-
-  /**
-   * Unset the filter filter function.
-   *
-   * @return void
-   */
-  public function unsetFilter () {
-    $this->_filter = null;
-  }
-
-  /**
-   * Close the reader.
-   *
-   * @return void
-   */
-  public function close () {
-    $this->_isOpen = false;
-  }
-
-  /**
-   * Return TRUE if the reader is open.
-   *
-   * @return boolean TRUE if the reader is open
-   */
-  public function isOpen () {
-    return $this->_isOpen;
-  }
-}
\ No newline at end of file
diff --git a/src/tests/.empty b/src/tests/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/tests/functional-tests/.empty b/src/tests/functional-tests/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/tests/integration-tests/.empty b/src/tests/integration-tests/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/tests/unit-tests/.empty b/src/tests/unit-tests/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/tests/unit-tests/bin/.empty b/src/tests/unit-tests/bin/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/tests/unit-tests/bootstrap.php b/src/tests/unit-tests/bootstrap.php
deleted file mode 100644
index 97c36d207cb52450e1529903565cb313b323d081..0000000000000000000000000000000000000000
--- a/src/tests/unit-tests/bootstrap.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-// =========================================================================
-//
-// tests/bootstrap.php
-//		A helping hand for running our unit tests
-//
-// Author	Stuart Herbert
-//		(stuart@stuartherbert.com)
-//
-// Copyright	(c) 2011 Stuart Herbert
-//		Released under the New BSD license
-//
-// =========================================================================
-
-// step 1: create the APP_TOPDIR constant that all components require
-define('APP_TOPDIR', realpath(__DIR__ . '/../../php'));
-define('APP_LIBDIR', realpath(__DIR__ . '/../../../vendor/php'));
-define('APP_TESTDIR', realpath(__DIR__ . '/php'));
-
-// step 2: find the autoloader, and install it
-require_once(APP_LIBDIR . '/psr0.autoloader.php');
-
-// step 3: add the additional paths to the include path
-psr0_autoloader_searchFirst(APP_LIBDIR);
-psr0_autoloader_searchFirst(APP_TESTDIR);
-psr0_autoloader_searchFirst(APP_TOPDIR);
-
-// step 4: enable ContractLib if it is available
-if (class_exists('Phix_Project\ContractLib\Contract'))
-{
-        \Phix_Project\ContractLib\Contract::EnforceWrappedContracts();
-}
-
-// step 5: define a constant with the fixture directory
-define('APP_FIXTUREDIR', realpath(__DIR__ . '/../fixtures'));
-
-// step 6: Set error level to include E_STRICT
-\error_reporting(\E_ALL | \E_STRICT);
\ No newline at end of file
diff --git a/src/tests/unit-tests/php/.empty b/src/tests/unit-tests/php/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/tests/unit-tests/php/HAB/Pica/Reader/PicaPlainReaderTest.php b/src/tests/unit-tests/php/HAB/Pica/Reader/PicaPlainReaderTest.php
deleted file mode 100644
index 652aa37cdd6b24b3072afb6a069a530b34eef00e..0000000000000000000000000000000000000000
--- a/src/tests/unit-tests/php/HAB/Pica/Reader/PicaPlainReaderTest.php
+++ /dev/null
@@ -1,121 +0,0 @@
-<?php
-
-/**
- * Unit test for the PicaPlainReader class.
- *
- * This file is part of PicaReader.
- *
- * PicaReader 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.
- *
- * PicaReader 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 PicaReader.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   PicaReader
- * @author    David Maus <maus@hab.de>
- * @copyright Copyright (c) 2012 by Herzog August Bibliothek Wolfenbüttel
- * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
- */
-
-namespace HAB\Pica\Reader;
-
-class PicaPlainReaderTest extends \PHPUnit_FrameWork_TestCase {
-
-  protected $_reader;
-
-  public function SetUp () {
-    $this->_reader = new PicaPlainReader();
-  }
-
-  public function testRead () {
-    $this->_reader->open($this->getFixture('single_record'));
-    $record = $this->_reader->read();
-    $this->assertInstanceOf('\HAB\Pica\Record\TitleRecord', $record);
-    $this->assertFalse($this->_reader->read());
-    $this->assertEquals(377, count($record->getFields()));
-    $this->assertEquals(21, count($record->getLocalRecords()));
-    $this->assertEquals('024836885', $record->getPPN());
-    $this->assertEquals(3, count($record->getLocalRecordByILN(31)->getCopyRecords()));
-  }
-
-  public function testReadDoubleEncodedDollarSign () {
-    $this->_reader->open('002@/00 $0T$adouble$$dollar');
-    $record = $this->_reader->read();
-    $field = $record->getFirstMatchingField('002@/00');
-    $subfield = $field->getNthSubfield('a', 0);
-    $this->assertEquals('double$dollar', $subfield->getValue());
-  }
-
-  public function testReadDoubleEncodedDoubleDollarSign2x () {
-    $this->_reader->open('002@/00 $0T$adouble$$$$dollar');
-    $record = $this->_reader->read();
-    $field = $record->getFirstMatchingField('002@/00');
-    $subfield = $field->getNthSubfield('a', 0);
-    $this->assertEquals('double$$dollar', $subfield->getValue());
-  }
-
-  public function testReadDoubleEncodedDoubleDollarSignAtEnd () {
-    $this->_reader->open('002@/00 $0T$adoubledollar$$');
-    $record = $this->_reader->read();
-    $field = $record->getFirstMatchingField('002@/00');
-    $subfield = $field->getNthSubfield('a', 0);
-    $this->assertEquals('doubledollar$', $subfield->getValue());
-  }
-
-  public function testReadDoubleEncodedDoubleDollarSignOnly () {
-    $this->_reader->open('002@/00 $0T$a$$');
-    $record = $this->_reader->read();
-    $field = $record->getFirstMatchingField('002@/00');
-    $subfield = $field->getNthSubfield('a', 0);
-    $this->assertEquals('$', $subfield->getValue());
-  }
-
-  public function testReadFilterInvalidSubfieldCode () {
-    $filter = function (array $record) {
-      return array('fields' => array_map(function (array $field) {
-            return array('tag' => $field['tag'],
-                         'occurrence' => $field['occurrence'],
-                         'subfields' => array_filter($field['subfields'],
-                                                    function (array $subfield) {
-                                                      return \HAB\Pica\Record\Subfield::isValidSubfieldCode($subfield['code']);
-                                                    }));
-          }, $record['fields']));
-    };
-    $this->_reader->open("002@/00 \$0T\n000A/00 \$@FOOBAR");
-    $this->_reader->setFilter($filter);
-    $this->assertSame($filter, $this->_reader->getFilter());
-    $this->_reader->read();
-    $this->_reader->unsetFilter();
-  }
-
-  ///
-
-  /**
-   * @expectedException \RuntimeException
-   */
-  public function testReadMalformedSingleDollarAtEnd () {
-    $this->_reader->open('002@/00 $0T$aFOOBAR$');
-    $record = $this->_reader->read();
-  }
-  /**
-   * @expectedException \RuntimeException
-   */
-  public function testReadMalformedLine () {
-    $this->_reader->open('');
-    $this->_reader->read();
-  }
-
-  ///
-
-  protected function getFixture ($fixture) {
-    return file_get_contents(\APP_FIXTUREDIR . DIRECTORY_SEPARATOR . "{$fixture}.pp");
-  }
-
-}
diff --git a/src/tests/unit-tests/php/HAB/Pica/Reader/PicaXmlReaderTest.php b/src/tests/unit-tests/php/HAB/Pica/Reader/PicaXmlReaderTest.php
deleted file mode 100644
index 71e7dc215a2d0861e3161de774570245dd51d9a9..0000000000000000000000000000000000000000
--- a/src/tests/unit-tests/php/HAB/Pica/Reader/PicaXmlReaderTest.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-/**
- * Unit test for the PicaPlainReader class.
- *
- * This file is part of PicaReader.
- *
- * PicaReader 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.
- *
- * PicaReader 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 PicaReader.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   PicaReader
- * @author    David Maus <maus@hab.de>
- * @copyright Copyright (c) 2012 by Herzog August Bibliothek Wolfenbüttel
- * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
- */
-
-namespace HAB\Pica\Reader;
-
-class PicaXmlReaderTest extends \PHPUnit_FrameWork_TestCase {
-
-  protected $_reader;
-
-  public function SetUp () {
-    $this->_reader = new PicaXmlReader();
-  }
-
-  public function testRead () {
-    $this->_reader->open($this->getFixture('single_record'));
-    $record = $this->_reader->read();
-    $this->assertInstanceOf('\HAB\Pica\Record\TitleRecord', $record);
-    $this->assertFalse($this->_reader->read());
-    $this->assertEquals(377, count($record->getFields()));
-    $this->assertEquals(21, count($record->getLocalRecords()));
-    $this->assertEquals('024836885', $record->getPPN());
-    $this->assertEquals(3, count($record->getLocalRecordByILN(31)->getCopyRecords()));
-  }
-
-  protected function getFixture ($fixture) {
-    return file_get_contents(\APP_FIXTUREDIR . DIRECTORY_SEPARATOR . "{$fixture}.xml");
-  }
-
-}
\ No newline at end of file
diff --git a/src/tests/unit-tests/www/.empty b/src/tests/unit-tests/www/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/www/.empty b/src/www/.empty
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 0000000000000000000000000000000000000000..cf31ad8863e204daa01def64e08cdf71eb7298fe
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * This file is part of PicaReader.
+ *
+ * PicaReader 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.
+ *
+ * PicaReader 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 PicaReader.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author    David Maus <maus@hab.de>
+ * @copyright Copyright (c) 2013 by Herzog August Bibliothek Wolfenbüttel
+ * @license   http://www.gnu.org/licenses/gpl.txt GNU General Public License v3
+ */
+
+require_once realpath(__DIR__ . '/../vendor/autoload.php');
+
+define('PHPUNIT_FIXTURES', realpath(__DIR__ . '/fixtures'));
+
+$loader = new Composer\Autoload\ClassLoader();
+$loader->add('HAB', realpath(__DIR__ . '/src'));
+$loader->register();
diff --git a/src/tests/fixtures/single_record.pp b/tests/fixtures/single_record.pp
similarity index 100%
rename from src/tests/fixtures/single_record.pp
rename to tests/fixtures/single_record.pp
diff --git a/src/tests/fixtures/single_record.xml b/tests/fixtures/single_record.xml
similarity index 100%
rename from src/tests/fixtures/single_record.xml
rename to tests/fixtures/single_record.xml
diff --git a/tests/src/HAB/Pica/Reader/PicaPlainReaderTest.php b/tests/src/HAB/Pica/Reader/PicaPlainReaderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0dd65f26ad54216b33361935e655a916079ce1cf
--- /dev/null
+++ b/tests/src/HAB/Pica/Reader/PicaPlainReaderTest.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * Unit test for the PicaPlainReader class.
+ *
+ * This file is part of PicaReader.
+ *
+ * PicaReader 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.
+ *
+ * PicaReader 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 PicaReader.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   PicaReader
+ * @author    David Maus <maus@hab.de>
+ * @copyright Copyright (c) 2012, 2013 by Herzog August Bibliothek Wolfenbüttel
+ * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
+ */
+
+namespace HAB\Pica\Reader;
+
+use PHPUnit_FrameWork_TestCase ;
+
+class PicaPlainReaderTest extends PHPUnit_FrameWork_TestCase
+{
+
+    protected $_reader;
+
+    public function setup ()
+    {
+        $this->_reader = new PicaPlainReader();
+    }
+
+    public function testRead ()
+    {
+        $this->_reader->open($this->getFixture('single_record'));
+        $record = $this->_reader->read();
+        $this->assertInstanceOf('\HAB\Pica\Record\TitleRecord', $record);
+        $this->assertFalse($this->_reader->read());
+        $this->assertEquals(377, count($record->getFields()));
+        $this->assertEquals(21, count($record->getLocalRecords()));
+        $this->assertEquals('024836885', $record->getPPN());
+        $this->assertEquals(3, count($record->getLocalRecordByILN(31)->getCopyRecords()));
+    }
+
+    public function testReadDoubleEncodedDollarSign ()
+    {
+        $this->_reader->open('002@/00 $0T$adouble$$dollar');
+        $record = $this->_reader->read();
+        $field = $record->getFirstMatchingField('002@/00');
+        $subfield = $field->getNthSubfield('a', 0);
+        $this->assertEquals('double$dollar', $subfield->getValue());
+    }
+
+    public function testReadDoubleEncodedDoubleDollarSign2x ()
+    {
+        $this->_reader->open('002@/00 $0T$adouble$$$$dollar');
+        $record = $this->_reader->read();
+        $field = $record->getFirstMatchingField('002@/00');
+        $subfield = $field->getNthSubfield('a', 0);
+        $this->assertEquals('double$$dollar', $subfield->getValue());
+    }
+
+    public function testReadDoubleEncodedDoubleDollarSignAtEnd ()
+    {
+        $this->_reader->open('002@/00 $0T$adoubledollar$$');
+        $record = $this->_reader->read();
+        $field = $record->getFirstMatchingField('002@/00');
+        $subfield = $field->getNthSubfield('a', 0);
+        $this->assertEquals('doubledollar$', $subfield->getValue());
+    }
+
+    public function testReadDoubleEncodedDoubleDollarSignOnly ()
+    {
+        $this->_reader->open('002@/00 $0T$a$$');
+        $record = $this->_reader->read();
+        $field = $record->getFirstMatchingField('002@/00');
+        $subfield = $field->getNthSubfield('a', 0);
+        $this->assertEquals('$', $subfield->getValue());
+    }
+
+    public function testReadFilterInvalidSubfieldCode ()
+    {
+        $filter = function (array $record) {
+            return array('fields' => array_map(function (array $field) {
+                return array('tag' => $field['tag'],
+                             'occurrence' => $field['occurrence'],
+                             'subfields' => array_filter($field['subfields'],
+                                                         function (array $subfield) {
+                                                         return \HAB\Pica\Record\Subfield::isValidSubfieldCode($subfield['code']);
+                                                     }));
+            }, $record['fields']));
+        };
+        $this->_reader->open("002@/00 \$0T\n000A/00 \$@FOOBAR");
+        $this->_reader->setFilter($filter);
+        $this->assertSame($filter, $this->_reader->getFilter());
+        $this->_reader->read();
+        $this->_reader->unsetFilter();
+    }
+
+    ///
+
+    /**
+     * @expectedException RuntimeException
+     */
+    public function testReadMalformedSingleDollarAtEnd ()
+    {
+        $this->_reader->open('002@/00 $0T$aFOOBAR$');
+        $record = $this->_reader->read();
+    }
+    /**
+     * @expectedException RuntimeException
+     */
+    public function testReadMalformedLine ()
+    {
+        $this->_reader->open('');
+        $this->_reader->read();
+    }
+
+    ///
+
+    protected function getFixture ($fixture)
+    {
+        return file_get_contents(\PHPUNIT_FIXTURES . DIRECTORY_SEPARATOR . "{$fixture}.pp");
+    }
+}
diff --git a/tests/src/HAB/Pica/Reader/PicaXmlReaderTest.php b/tests/src/HAB/Pica/Reader/PicaXmlReaderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..576eabfac51e57d56688767e4394b39f9ddef842
--- /dev/null
+++ b/tests/src/HAB/Pica/Reader/PicaXmlReaderTest.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * Unit test for the PicaPlainReader class.
+ *
+ * This file is part of PicaReader.
+ *
+ * PicaReader 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.
+ *
+ * PicaReader 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 PicaReader.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   PicaReader
+ * @author    David Maus <maus@hab.de>
+ * @copyright Copyright (c) 2012, 2013 by Herzog August Bibliothek Wolfenbüttel
+ * @license   http://www.gnu.org/licenses/gpl.html GNU General Public License v3
+ */
+
+namespace HAB\Pica\Reader;
+
+use PHPUnit_FrameWork_TestCase;
+
+class PicaXmlReaderTest extends PHPUnit_FrameWork_TestCase
+{
+
+    protected $_reader;
+
+    public function setup ()
+    {
+        $this->_reader = new PicaXmlReader();
+    }
+
+    public function testRead ()
+    {
+        $this->_reader->open($this->getFixture('single_record'));
+        $record = $this->_reader->read();
+        $this->assertInstanceOf('\HAB\Pica\Record\TitleRecord', $record);
+        $this->assertFalse($this->_reader->read());
+        $this->assertEquals(377, count($record->getFields()));
+        $this->assertEquals(21, count($record->getLocalRecords()));
+        $this->assertEquals('024836885', $record->getPPN());
+        $this->assertEquals(3, count($record->getLocalRecordByILN(31)->getCopyRecords()));
+    }
+
+    protected function getFixture ($fixture)
+    {
+        return file_get_contents(\PHPUNIT_FIXTURES . DIRECTORY_SEPARATOR . "{$fixture}.xml");
+    }
+}
\ No newline at end of file