diff --git a/.editorconfig b/.editorconfig index 507e5f7b5..d43b16be5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,27 +14,28 @@ # limitations under the License. # -root=true +root = true [*] -charset=utf-8 -end_of_line=lf -insert_final_newline=true -trim_trailing_whitespace=true -indent_style=space -indent_size=4 -continuation_indent_size=8 +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true -[{*.yml,*.yaml}] -indent_style=space -indent_size=2 +[*.bat] +end_of_line = crlf +trim_trailing_whitespace = false -[*.gradle] -indent_style=space -indent_size=2 +[*.{gradle,yml}] +indent_size = 2 -[*.bat] -end_of_line=crlf +[metafacture-io/src/test/resources/org/metafacture/io/compressed.txt] +insert_final_newline = false + +[metamorph/src/test/resources/org/metafacture/metamorph/maps/file-map-test.txt] +trim_trailing_whitespace = false [metafacture-runner/src/main/dist/config/java-options.conf] -end_of_line=crlf +end_of_line = crlf diff --git a/.gitattributes b/.gitattributes index 484ce04f8..6866584b8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -28,7 +28,7 @@ *.js text *.fom text -# Platform-specific scripts should always use +# Platform-specific scripts should always use # their native end-of-line markers: *.bat text eol=crlf *.sh text eol=lf diff --git a/.gitignore b/.gitignore index 35e35cb1a..828e6a7b7 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,9 @@ target/ .gradletasknamecache build +# Ignore tmp directory +tmp + # Ignore Eclipse project files: .checkstyle .pmd @@ -44,3 +47,10 @@ atlassian-ide-plugin.xml # Ignore files generated by CI: buildbot.keyring + +# Ignore VS code/codium config files +.vscode + +# Ignore files with sensitive data +gradle.properties +secring.gpg diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..3122987cf --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,222 @@ +# Contributing to Metafacture + +Welcome! Thank you for contributing to Metafacture. + +The following is a set of guidelines for contributing to Metafacture and how to work together in an efficient and goal-oriented way. We use the simple GitHub workflow: the master branch is always in a working state. New features are developed in feature branches which are merged into the master after review in pull requests. See details on the [GitHub flow](https://guides.github.com/introduction/flow/). The agile methods we use are inspired by the [Scrum Guide](https://www.scrum.org/resources/scrum-guide). + +## Table of Contents + +[How can I contribute?](#how-can-i-contribute) +* [Reporting Bugs](#reporting-bugs) +* [Suggesting Enhancements](#suggesting-enhancements) +* [Contributing Code](#contributing-code) +* [Improving Documentation](#improving-documentation) + +[Maintainer Guidelines](#maintainer-guidelines) +* [Board and Issues](#board-and-issues) +* [From Backlog to Done](#from-backlog-to-done) +* [Definition of Ready](#definition-of-ready) +* [Definition of Done](#definition-of-done) +* [Releasing Metafacture](#releasing-metafacture) + + +## How can I contribute? + +### Reporting Bugs + +This section guides you through submitting a bug report for Metafacture. Following these guidelines helps maintainers and the community understand your report, reproduce the behavior and find related reports. + +Before creating bug reports, please check if an issue with this bug already exists in the appropriate repository, [e.g. metafacture-core](https://github.com/metafacture/metafacture-core/issues). When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-bug-report). + +**Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. + +#### How Do I Submit A Bug Report? + +Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create a new issue on the appropriate repository, [e.g. metafacture-core](https://github.com/metafacture/metafacture-core/issues/new). + +Explain the problem and include additional details to help maintainers reproduce the problem: + +* **Use a clear and descriptive title** for the issue to identify the problem. +* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you are running Metafacture (e.g. running flux.sh/flux.bat or as a Java library). When listing steps, **don't just say what you did, but explain how you did it**. For example, if you're describing the behavior of a specific Morph/Fix function, provide the actual workflow (Flux/Java) and the full Morph file you're using (see also next point). +* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code). The ideal example would be a stripped-down runnable use case using some sample data showing the problematic behavior. +* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +* **Explain which behavior you expected to see instead and why.** +* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened. + +### Suggesting Enhancements + +This section guides you through submitting an enhancement suggestion for Metafacture, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions. + +Before creating enhancement suggestions, please **perform a [cursory search](https://github.com/search?q=is%3Aissue+org%3Ametafacture)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. When you are creating an enhancement suggestion, please include as many details as possible: + +#### How Do I Submit An Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create a new issue on the appropriate repository, [e.g. metafacture-core](https://github.com/metafacture/metafacture-core/issues/new). + +Provide the following information: + +* **Use a clear and descriptive title** for the issue to identify the suggestion. +* **Provide a step-by-step description of the suggested enhancement** in as many details as possible. +* **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code). +* **Describe the current behavior** and **explain which behavior you expected to see instead** and why. +* **Explain why this enhancement would be useful** to most users. +* **List any other applications where this enhancement exists.** + +### Contributing Code + +The overall code contribution process is: + +1. Fork the appropriate metafacture repo and clone your fork (members with write access: clone the appropriate metafacture repo) +1. In your local clone, switch to a new feature branch for the bug fix or feature enhancement you want to implement. The name of the feature branch should start with the corresponding issue number and contain additional info for convenience (using camelCaseFormatting, e.g. '111-featureDesciption') +1. Implement, test, and build your bug fix or feature enhancement +1. Commit your changes and push the feature branch to your fork (members with write access: push to the appropriate metafacture repo) +1. Open a pull request for your feature branch to submit your changes (see details below) + +#### Pull Requests + +Before submitting your changes, make sure you can successfully [build from source](https://github.com/metafacture/metafacture-core#building-metafacture-core-from-source). + +Please follow these steps to [propose contributions to the project](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests). + +When submitting a pull request use a meaningful title and use [closing keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) in the pull request description to reference the issue it is meant to resolve. +After you submit your pull request, verify that all [GitHub Actions](https://docs.github.com/en/actions) are passing. If an action is failing, and you believe that the failure is unrelated to your change, please leave a comment on the pull request explaining why you believe the failure is unrelated. If the failure was a false positive, we will open an issue to track that problem with the GitHub Actions. + +The reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. + +#### Git conventions + +Git commits should be as granular as possible. When working on a fix for issue X, we try not to add other things we notice (formatting, refactorings, etc.) to the same commit. Those things should be placed in an own commit to the same branch. Commits should always reference a related issue (like "#111"). This makes each specific change easier to review and understand in the future. + +##### Commit Messages + +* Use the imperative mood in a subject line ("Add feature" not "Added feature") +* Separate subject from body with a blank line +* Capitalize the subject line +* Do not end the subject line with a period +* Wrap lines at 72 characters +* Use the body to explain *what* and *why* not *how* (which can be seen in the diff) +* Reference issues in the same repository at the end of the first line using the short form (e.g. #14) + +For details, see [these](https://chris.beams.io/posts/git-commit/) [posts](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). + +##### Force Pushing + +As a general rule, we don't change public commit history, i.e. we don’t use ```--force``` or ```-f``` with ```git push```. Local amending and rebasing before pushing to GitHub is no problem and will not require to ```--force``` when pushing. While we consider this general rule as directive, we condone force pushing as long as the branch has no open pull request yet and only one person is working on this branch. In case of a force push we use ```--force-with-lease``` to ensure that we do not overwrite any remote commits. If rewriting is required in an open pull request, instead of force pushing we open a new branch based on main and ```cherry-pick``` commits or add new code in this branch. The existing pull request is then closed. + +#### Code Formatting and Quality + +Please format your code according to [this EditorConfig file](https://github.com/metafacture/metafacture-fix/blob/master/.editorconfig) and consider our current [code quality and style guidelines](https://github.com/metafacture/metafacture-core/wiki/Code-Quality-and-Style). + +The [metafacture-fix build](https://github.com/metafacture/metafacture-fix/blob/master/build.gradle) performs automated [EditorConfig](https://github.com/metafacture/metafacture-fix/blob/master/.editorconfig) and [Checkstyle](https://github.com/metafacture/metafacture-fix/blob/master/config/checkstyle/checkstyle.xml) checks. + +The code is automatically [quality-checked on sonarcloud.io](https://sonarcloud.io/dashboard?id=org.metafacture%3Ametafacture-core) when pushed to GitHub. + +### Improving Documentation + +If you notice errors, inconsistencies or missing parts in the documentation, we are very happy about improvements. Please follow the general process for contributing code from above, or open an issue. + +## Maintainer Guidelines + +### Board and Issues + +We use the [Metafacture Board](https://github.com/orgs/metafacture/projects/1) to track the progress of Metafacture issues. In the following we describe when issues are ready and what stages to pass to make a issue done. + +### From Backlog to Done + +Issues move from left to right. We use the following columns: + +#### Backlog + +Here are all issues that are planned but not ready, have open questions and/or dependencies on other issues or on an external resource. As an open community project, we don't want to reject suggestions or requests due to missing resources. At the same time, we can't promise to implement all issues in the backlog. + +#### Ready + +An issue is ready if it’s possible to start working on it according to the [Definition of Ready](#definition-of-ready). Prioritized items (like bugs) are moved to the top of the *Ready* column. The assignee must re-verify the readiness when moving the item from *Ready* to *Working*, especially when an item has been in the 'Ready'-column for a long time. + +#### Working + +When we start working on an issue, we move it to the working column. Ideally, every person should only work on one issue at a time. That way the working column provides an overview of who is currently working on what. Issues are only moved into or out of the working column by the person who is assigned. Issues in working are only reassigned by the person who is currently assigned. If the assignee thinks the issue is ready for review they add instructions and links for testing the changed behavior in the issue, move it to the *Review* column, assign the previously announced functional reviewer (see [Definition of Ready](#definition-of-ready)), and open an unassigned pull request for the feature branch. For details, see the above section on [contributing code](#contributing-code). + +#### Review + +There are two kinds of reviews: first, a functional review (which happens on the issue) and second, a code review (which happens on the pull request). + +##### Functional Review + +In functional review, the actual behavior of the bugfix or the new feature is reviewed. If the reviewer finds problems, these should be described by providing links, examples, or screenshots that show the behavior, and then reassigns the team member that assigned the issue for review, leaving the issue in the review column. If everything works as expected, the reviewer posts a +1 comment on the issue, removes their assignment and makes the suggested code reviewer both assignee and reviewer of the linked pull request. The issue remains unassigned. + +##### Code Review + +In code review, the technical implementation of the bugfix or the new feature is reviewed. Changes during the review process are created in additional commits which are pushed to the feature branch. They are added to the existing pull request automatically. At the end of the code review, the reviewer approves the pull request and reassigns the pull request to its original creator. + +#### Done + +The creator of the pull request merges the pull request after checking the [Definition of Done](#definition-of-done). After the merge, the issue and the linked pull request are closed and moved to the *Done* column automatically (due to the [closing keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) used in the pull request description). We delete feature branches after merging. + +### Definition of Ready + +The *Definition of Ready* describes a list of criteria which issues have to meet to move from column 'Backlog' to 'Ready': + +* The person who will implement the issue is assigned and has every information to work on this issue. Only the assignee can move the issue to Ready. +* The person who will do code review is mentioned in the issue, e.g. in a comment like "code could be reviewed by ..." (functional review will typically be done by the person reporting the issue when they verify the fix). +* There are no blocking dependencies. Dependencies are expressed through simple referencing of the blocking issue (e.g. depends on #111), see details on [autolinked references and URLs](https://docs.github.com/en/github/writing-on-github/autolinked-references-and-urls). + +### Definition of Done + +The *Definition of Done* describes a list of criteria which issues have to meet to be called 'Done': + +* Functionality reviewed (approved by user/product owner) +* Documentation exists (external documentation must be linked) +* GitHub Actions / CI passed (contains tests) +* Pull request is reviewed and approved +* Functionality is merged into the master branch + +### Releasing Metafacture + +We use semantic versioning in release numbers `A`.`B`.`C`, i.e. increase `A` when it's a major release breaking backward compatibility; increase `B` when it got new features; increase `C` indicating bug-fixes. + +#### Build and publish to GitHub + +The following commands trigger a release build. + +1. Create a signed tag: + ``` + git tag -s metafacture-core-A.B.C + ``` +1. When prompted, add a sensible commit message. For instance, something like: + ``` + Publish first release of the Metafacture A line + ``` +1. You can now test the build locally by invoking: + ``` + ./gradlew assemble + ``` +1. Finally, push the new tag to GitHub to trigger the actual release build: + ``` + git push --follow-tags metafacture-core-A.B.C + ``` + +#### Publish to Maven Central + +Upload archives to sonatype (where they can be released to Maven Central) + +1. Make sure to have a clean directory (otherwise only a SNAPSHOT will be built): + ``` + git status + ``` +1. You need a `gradle.properties` in the root directory that looks like this: + ``` + signing.gnupg.executable=gpg + signing.gnupg.useLegacyGpg=true + signing.gnupg.homeDir=$e.g."~/.gnupg" + signing.gnupg.keyName=$yourKeyName + signing.gnupg.passphrase=$keysPassphrase + releaseRepositoryUrl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ + releaseRepositoryUser=$yourSonatypeUsername + releaseRepositoryPassword=$yourSonatypePassword + ``` +1. Let the release be built, signed and uploaded: + ``` + ./gradlew uploadArchives + ``` +1. Finally, go to oss.sonatype.org , check the `Staging Repositories` when it's finished, and release it by clicking `close` diff --git a/README.md b/README.md index c81d9d638..c40a83b63 100644 --- a/README.md +++ b/README.md @@ -2,45 +2,45 @@ [![Build](https://github.com/metafacture/metafacture-core/workflows/Build/badge.svg?branch=master)](https://github.com/metafacture/metafacture-core/actions?query=workflow%3ABuild) [![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=org.metafacture:metafacture-core&metric=alert_status)](https://sonarcloud.io/dashboard/index/org.metafacture:metafacture-core) -Metafacture is a toolkit for processing semi-structured data with a focus on library metadata. It provides a versatile set of tools for reading, writing and transforming data. Metafacture can be used as a stand-alone application or as a Java library in other applications. The name Metafacture is a portmanteau of the words *meta* data and manu*facture*. +Metafacture is a toolkit for processing semi-structured data with a focus on library metadata. It provides a versatile set of tools for reading, writing and transforming data. Metafacture can be used as a stand-alone application or as a Java library in other applications. The name Metafacture is a portmanteau of the words *meta*data and manu*facture*. -Metafacture includes a [large number of modules](https://github.com/metafacture/metafacture-documentation/blob/master/flux-commands.md) for operating on semi-structured data. These modules can be combined to build pipelines to perform complex metadata processing tasks. The pipelines can be constructed either in Java code or with the domain-specific language **Flux**. One of the core features of Metafacture is the **Metamorph** module. Metamorph is an xml-based language for specifying transformations of semi-structured data. It can be seamlessly integrated into Java code. +Metafacture includes a [large number of modules](https://github.com/metafacture/metafacture-documentation/blob/master/flux-commands.md) for operating on semi-structured data. These modules can be combined to build pipelines to perform complex metadata processing tasks. The pipelines can be constructed either in Java code or with the domain-specific language **Flux**. One of the core features of Metafacture is the **Metamorph** module. Metamorph is an XML-based language for specifying transformations of semi-structured data. It can be seamlessly integrated into Java code. At its heart Metafacture is a framework for implementing modules for metadata processing. This makes Metafacture easily extendable with additional modules. The [plugins and tools page](https://github.com/metafacture/metafacture-core/wiki/Plugins-and-Tools) on the wiki shows supplementary packages and projects which extend Metafacture. -Originally, Metafacture was developed as part of the [Culturegraph](http://culturegraph.org) platform but it is developed independently now and used by others, too: [see who uses Metafacture](https://github.com/metafacture/metafacture-core/wiki/Who-uses-Metafacture). +Originally, Metafacture was developed as part of the [Culturegraph](http://www.culturegraph.org) platform but it is developed independently now and used by others, too: [see who uses Metafacture](https://github.com/metafacture/metafacture-core/wiki/Who-uses-Metafacture). # Getting started You can either use Metafacture as a stand-alone application or include it as a Java library in your own projects. ## Metafacture as a stand-alone application - -If you are only interested in running Flux scripts without doing any Java programming this is the way to go. The instructions assume that you are using a *nix-like shell. -1. Download the latest distribution package from the [metafacture-core/releases](https://github.com/metafacture/metafacture-core/releases) page. Make sure that you do download a distribution package and _not_ a source code package (the file name should include *-dist*). +If you are only interested in running Flux scripts without doing any Java programming this is the way to go. The instructions assume that you are using a \*nix-like shell. [See more information in the wiki page about Flux](https://github.com/metafacture/metafacture-core/wiki/Flux-user-guide). + +1. Download the latest distribution package from the [release page](https://github.com/metafacture/metafacture-core/releases). Make sure that you do download a distribution package and *not* a source code package (the file name should include `*-dist*`). 2. Extract the downloaded archive: ```bash - $ tar xzf metafacture-core-VERSION-dist.tar.gz + $ tar xzf metafacture-core-$VERSION-dist.tar.gz ``` - This will create a new directory containing a ready-to-use metafacture distribution. + This will create a new directory containing a ready-to-use Metafacture distribution. 3. Change into the newly created directory: ```bash - $ cd metafacture-core-VERSION + $ cd metafacture-core-$VERSION ``` 4. Run one of the example scripts: ```bash $ ./flux.sh examples/read/marc21/read-marc21.flux ``` - This example will print a number of marc21 records on standard out. + This example will print a number of MARC 21 records on standard output. -The _examples_ folder contains many more examples which provide a good starting point for learning metafacture. If you have any questions please join our [mailing list](http://lists.dnb.de/mailman/listinfo/metafacture) or use our issue-based discussion forum over at [metafacture-documentation](https://github.com/metafacture/metafacture-documentation). +The `examples` folder contains many more examples which provide a good starting point for learning Metafacture. If you have any questions please join our [mailing list](http://lists.dnb.de/mailman/listinfo/metafacture) or use our issue-based discussion forum over at [metafacture-documentation](https://github.com/metafacture/metafacture-documentation). -## Using Metafacture as a Java libary +## Using Metafacture as a Java library -If you want use Metafacture in your own Java projects all you need to add some dependencies to your project. As of Metafacture 5 the single metafacture-core package has been replaced with a number of domain-specific packages. You can find the list of packages on [Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.metafacture%22). +If you want to use Metafacture in your own Java projects all you need is to add some dependencies to your project. As of Metafacture 5, the single metafacture-core package has been replaced with a number of domain-specific packages. You can find the list of packages on [Maven Central](https://search.maven.org/search?q=g:org.metafacture). Alternatively, you can simply guess the package names from the top-level folders in the source code repository -- they are the same. For instance, if you want to use Metamorph in your project, simply add the following dependency to your `pom.xml`: @@ -48,7 +48,7 @@ Alternatively, you can simply guess the package names from the top-level folders org.metafacture metamorph - VERSION + $VERSION ``` @@ -56,11 +56,11 @@ or if Gradle is your build tool of choice use: ```groovy dependencies { - implementation 'org.metafacture:metamorph:VERSION' + implementation 'org.metafacture:metamorph:$VERSION' } ``` -Our integration server automatically publishes successful builds of all branches as snapshot versions on [Sonatype OSS Repository](https://oss.sonatype.org/index.html#nexus-search;quick~metafacture). The version number is derived from the branch name. Snapshot builds from the master branch always have the version "master-SNAPSHOT". +Occasionally, we publish snapshot builds on [Sonatype OSS Repository](https://oss.sonatype.org/index.html#nexus-search;gav~org.metafacture~~~~). The version number is derived from the branch name. Snapshot builds from the master branch always have the version `master-SNAPSHOT`. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metafacture-biblio/build.gradle b/metafacture-biblio/build.gradle index c34a362fe..3f27c9d99 100644 --- a/metafacture-biblio/build.gradle +++ b/metafacture-biblio/build.gradle @@ -26,7 +26,7 @@ dependencies { exclude group: 'xercesImpl', module: 'xercesImpl' exclude group: 'xml-apis', module: 'xml-apis' } - implementation 'log4j:log4j:1.2.12' + implementation 'log4j:log4j:1.2.17' implementation 'org.slf4j:slf4j-api:1.7.7' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:2.5.5' diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/AlephMabXmlHandler.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/AlephMabXmlHandler.java index e1911dbe5..1a54a80e2 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/AlephMabXmlHandler.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/AlephMabXmlHandler.java @@ -12,6 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio; import org.metafacture.framework.FluxCommand; @@ -21,6 +22,7 @@ import org.metafacture.framework.annotations.In; import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultXmlPipe; + import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -48,6 +50,12 @@ public final class AlephMabXmlHandler extends DefaultXmlPipe { private String currentTag = ""; private StringBuilder builder = new StringBuilder(); + /** + * Creates instance of {@link AlephMabXmlHandler}. + */ + public AlephMabXmlHandler() { + } + @Override public void characters(final char[] chars, final int start, final int length) throws SAXException { @@ -60,11 +68,14 @@ public void endElement(final String uri, final String localName, final String qN if (AlephMabXmlHandler.CONTROLLFIELD.equals(localName)) { getReceiver().literal(this.currentTag, this.builder.toString().trim()); getReceiver().endEntity(); - } else if (AlephMabXmlHandler.SUBFIELD.equals(localName)) { + } + else if (AlephMabXmlHandler.SUBFIELD.equals(localName)) { getReceiver().literal(this.currentTag, this.builder.toString().trim()); - } else if (AlephMabXmlHandler.DATAFIELD.equals(localName)) { + } + else if (AlephMabXmlHandler.DATAFIELD.equals(localName)) { getReceiver().endEntity(); - } else if (AlephMabXmlHandler.RECORD.equals(localName)) { + } + else if (AlephMabXmlHandler.RECORD.equals(localName)) { getReceiver().endRecord(); } } @@ -76,16 +87,20 @@ public void startElement(final String uri, final String localName, final String this.builder = new StringBuilder(); this.currentTag = ""; getReceiver().startEntity(attributes.getValue(AlephMabXmlHandler.DATAFIELD_ATTRIBUTE)); - } else if (AlephMabXmlHandler.SUBFIELD.equals(localName)) { + } + else if (AlephMabXmlHandler.SUBFIELD.equals(localName)) { this.builder = new StringBuilder(); this.currentTag = attributes.getValue(AlephMabXmlHandler.SUBFIELD_ATTRIBUTE); - } else if (AlephMabXmlHandler.DATAFIELD.equals(localName)) { - getReceiver().startEntity(attributes.getValue(AlephMabXmlHandler.DATAFIELD_ATTRIBUTE) - + attributes.getValue(AlephMabXmlHandler.INDICATOR1) - + attributes.getValue(AlephMabXmlHandler.INDICATOR2)); - } else if (AlephMabXmlHandler.RECORD.equals(localName)) { + } + else if (AlephMabXmlHandler.DATAFIELD.equals(localName)) { + getReceiver().startEntity(attributes.getValue(AlephMabXmlHandler.DATAFIELD_ATTRIBUTE) + + attributes.getValue(AlephMabXmlHandler.INDICATOR1) + + attributes.getValue(AlephMabXmlHandler.INDICATOR2)); + } + else if (AlephMabXmlHandler.RECORD.equals(localName)) { getReceiver().startRecord(""); - } else if (AlephMabXmlHandler.LEADER.equals(localName)) { + } + else if (AlephMabXmlHandler.LEADER.equals(localName)) { this.builder = new StringBuilder(); this.currentTag = AlephMabXmlHandler.LEADER; } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/AseqDecoder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/AseqDecoder.java index 5ebdb18b8..bb6e70c4a 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/AseqDecoder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/AseqDecoder.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio; import org.metafacture.framework.FluxCommand; @@ -31,11 +32,22 @@ @In(String.class) @Out(StreamReceiver.class) @FluxCommand("decode-aseq") -public final class AseqDecoder - extends DefaultObjectPipe { +public final class AseqDecoder extends DefaultObjectPipe { private static final String FIELD_DELIMITER = "\n"; + private static final int CATEGORY_BEGIN = 10; + private static final int CATEGORY_END = 15; + private static final int FIELD_CONTENT_BEGIN = 18; + private static final int RECORD_IDENTIFIER_BEGIN = 0; + private static final int RECORD_IDENTIFIER_END = 9; + + /** + * Creates an instance of {@link AseqDecoder}. + */ + public AseqDecoder() { + } + @Override public void process(final String record) { assert !isClosed(); @@ -44,16 +56,17 @@ public void process(final String record) { return; } final String[] lines = trimedRecord.split(FIELD_DELIMITER); - for (int i = 0; i < lines.length; i++) { + for (int i = 0; i < lines.length; ++i) { final String field = lines[i]; if (i == 0) { - getReceiver().startRecord(field.substring(0, 9)); + getReceiver().startRecord(field.substring(RECORD_IDENTIFIER_BEGIN, RECORD_IDENTIFIER_END)); } - final String category = field.substring(10, 15).trim(); - final String fieldContent = field.substring(18).trim(); + final String category = field.substring(CATEGORY_BEGIN, CATEGORY_END).trim(); + final String fieldContent = field.substring(FIELD_CONTENT_BEGIN).trim(); if (!fieldContent.startsWith("$$")) { getReceiver().literal(category, fieldContent); - } else { + } + else { getReceiver().startEntity(category); final String[] subfields = fieldContent.split("\\$\\$"); for (final String subfield : subfields) { diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/ComarcXmlHandler.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/ComarcXmlHandler.java index 15edf729c..72ecfce52 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/ComarcXmlHandler.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/ComarcXmlHandler.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio; -import java.util.ArrayList; -import java.util.List; +package org.metafacture.biblio; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.StreamReceiver; @@ -25,9 +23,13 @@ import org.metafacture.framework.annotations.In; import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultXmlPipe; + import org.xml.sax.Attributes; import org.xml.sax.SAXException; +import java.util.ArrayList; +import java.util.List; + /** * A class handling ComarcXML. Comarc is the MARC version used by the National * Library of Slovenia. It does not have a leader, instead most (all?) the @@ -61,14 +63,10 @@ public class ComarcXmlHandler extends DefaultXmlPipe { private String currentTag = ""; private StringBuilder builder = new StringBuilder(); - private class Entry { - String key; - String value; - - Entry(final String key, final String value) { - this.key = key; - this.value = value; - } + /** + * Creates an instance of {@link ComarcXmlHandler}. + */ + public ComarcXmlHandler() { } @Override @@ -78,42 +76,46 @@ public void startElement(final String uri, final String localName, if (SUBFIELD.equals(localName)) { this.builder = new StringBuilder(); this.currentTag = attributes.getValue("code"); - } else if (DATAFIELD.equals(localName) - && this.state == RECORD_INITIALISED) { + } + else if (DATAFIELD.equals(localName) && this.state == RECORD_INITIALISED) { getReceiver().startEntity( - attributes.getValue("tag") + attributes.getValue("ind1") - + attributes.getValue("ind2")); - } else if (RECORD.equals(localName) && NAMESPACE.equals(uri)) { + attributes.getValue("tag") + + attributes.getValue("ind1") + + attributes.getValue("ind2")); + } + else if (RECORD.equals(localName) && NAMESPACE.equals(uri)) { this.state = RECORD_NOT_INITIALISED; } } @Override + @SuppressWarnings("fallthrough") public void endElement(final String uri, final String localName, final String qName) throws SAXException { if (SUBFIELD.equals(localName)) { this.subfieldValues.add(new Entry(this.currentTag, this.builder .toString().trim())); - } else if (DATAFIELD.equals(localName)) { + } + else if (DATAFIELD.equals(localName)) { switch (this.state) { - case RECORD_NOT_INITIALISED: - super.getReceiver().startRecord(getFirstSubfield("x")); - super.getReceiver().startEntity("000 "); - this.state = RECORD_INITIALISED; - // this should fall through so that the entity 000 is properly - // ended - case RECORD_INITIALISED: - for (final Entry entry : this.subfieldValues) { - super.getReceiver().literal(entry.key, entry.value); - } - super.getReceiver().endEntity(); - this.subfieldValues.clear(); - break; - default: - throw new SAXException( - "State was not one of initialised or not initialised"); + case RECORD_NOT_INITIALISED: + super.getReceiver().startRecord(getFirstSubfield("x")); + super.getReceiver().startEntity("000 "); + this.state = RECORD_INITIALISED; + // fall through so that the entity 000 is properly ended + case RECORD_INITIALISED: + for (final Entry entry : this.subfieldValues) { + super.getReceiver().literal(entry.getKey(), entry.getValue()); + } + super.getReceiver().endEntity(); + this.subfieldValues.clear(); + break; + default: + throw new SAXException( + "State was not one of initialised or not initialised"); } - } else if (RECORD.equals(localName) && NAMESPACE.equals(uri)) { + } + else if (RECORD.equals(localName) && NAMESPACE.equals(uri)) { getReceiver().endRecord(); } } @@ -127,12 +129,30 @@ public void characters(final char[] chars, final int start, final int length) private String getFirstSubfield(final String subfieldCode) { String ret = null; for (final Entry entry : this.subfieldValues) { - if (subfieldCode.equals(entry.key)) { - ret = entry.value; + if (subfieldCode.equals(entry.getKey())) { + ret = entry.getValue(); break; } } return ret; } + private class Entry { + private final String key; + private final String value; + + Entry(final String key, final String value) { + this.key = key; + this.value = value; + } + + private String getKey() { + return key; + } + + private String getValue() { + return value; + } + } + } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/MabDecoder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/MabDecoder.java index 62169f664..6cdbf1e56 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/MabDecoder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/MabDecoder.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio; -import java.util.regex.Pattern; +package org.metafacture.biblio; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.FormatException; @@ -26,6 +25,7 @@ import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultObjectPipe; +import java.util.regex.Pattern; /** * Parses a raw Mab2 stream (utf-8 encoding assumed). Events are handled by a @@ -40,8 +40,7 @@ @In(String.class) @Out(StreamReceiver.class) @FluxCommand("decode-mab") -public final class MabDecoder - extends DefaultObjectPipe { +public final class MabDecoder extends DefaultObjectPipe { private static final String FIELD_END = "\u001e"; private static final Pattern FIELD_PATTERN = @@ -58,6 +57,12 @@ public final class MabDecoder private static final String ID_TAG = "001 "; private static final int TAG_LENGTH = 4; + /** + * Creates an instance of {@link MabDecoder}. + */ + public MabDecoder() { + } + @Override public void process(final String record) { assert !isClosed(); @@ -70,7 +75,7 @@ public void process(final String record) { try { getReceiver().literal(LEADER, record.substring(0, HEADER_SIZE)); - getReceiver().literal(TYPE, String.valueOf(record.charAt(HEADER_SIZE-1))); + getReceiver().literal(TYPE, String.valueOf(record.charAt(HEADER_SIZE - 1))); final String content = record.substring(HEADER_SIZE); for (final String part : FIELD_PATTERN.split(content)) { if (!part.startsWith(RECORD_END)) { @@ -80,7 +85,8 @@ public void process(final String record) { if (subFields.length == 1) { getReceiver().literal(fieldName, subFields[0]); - } else { + } + else { getReceiver().startEntity(fieldName); for (int i = 1; i < subFields.length; ++i) { @@ -92,7 +98,8 @@ public void process(final String record) { } } } - } catch (final IndexOutOfBoundsException e) { + } + catch (final IndexOutOfBoundsException e) { throw new FormatException("[" + record + "]", e); } @@ -100,13 +107,14 @@ public void process(final String record) { } private String extractIdFromRecord(final String record) { - try{ + try { final int fieldEnd = record.indexOf(FIELD_END, HEADER_SIZE); - if(record.substring(HEADER_SIZE, HEADER_SIZE + TAG_LENGTH).equals(ID_TAG)){ + if (record.substring(HEADER_SIZE, HEADER_SIZE + TAG_LENGTH).equals(ID_TAG)) { return record.substring(HEADER_SIZE + TAG_LENGTH, fieldEnd); } throw new MissingIdException(record); - } catch (IndexOutOfBoundsException e) { + } + catch (final IndexOutOfBoundsException e) { throw new FormatException(INVALID_FORMAT + record, e); } } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/OaiPmhOpener.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/OaiPmhOpener.java index 2ce807079..908e6d052 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/OaiPmhOpener.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/OaiPmhOpener.java @@ -1,4 +1,4 @@ -/* Copyright 2013 Pascal Christoph. +/* Copyright 2013, 2022 Pascal Christoph and others. * Licensed under the Eclipse Public License 1.0 */ package org.metafacture.biblio; @@ -10,7 +10,6 @@ import java.io.Reader; import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerException; import javax.xml.xpath.XPathException; import org.metafacture.framework.MetafactureException; @@ -24,104 +23,106 @@ /** * Opens an OAI-PMH stream and passes a reader to the receiver. - * + * * @author Pascal Christoph (dr0i) - * + * */ @Description("Opens an OAI-PMH stream and passes a reader to the receiver. Mandatory arguments are: BASE_URL, DATE_FROM, DATE_UNTIL, METADATA_PREFIX, SET_SPEC .") @In(String.class) @Out(java.io.Reader.class) -public final class OaiPmhOpener extends - DefaultObjectPipe> { - - private String encoding = "UTF-8"; - - final ByteArrayOutputStream OUTPUT_STREAM = new ByteArrayOutputStream(); - - private String dateFrom; - - private String dateUntil; - - private String setSpec; - - private String metadataPrefix; - - /** - * Default constructor - */ - public OaiPmhOpener() { - - } - - /** - * Sets the encoding to use. The default setting is UTF-8. - * - * @param encoding new default encoding - */ - public void setEncoding(final String encoding) { - this.encoding = encoding; - } - - /** - * Sets the beginning of the retrieving of updated data. The form is - * YYYY-MM-DD . - * - * @param dateFrom The form is YYYY-MM-DD . - */ - public void setDateFrom(final String dateFrom) { - this.dateFrom = dateFrom; - } - - /** - * Sets the end of the retrieving of updated data. The form is YYYY-MM-DD . - * - * @param dateUntil The form is YYYY-MM-DD . - */ - public void setDateUntil(final String dateUntil) { - this.dateUntil = dateUntil; - } - - /** - * Sets the OAI-PM metadata prefix . - * - * @param metadataPrefix the OAI-PM metadata prefix - */ - public void setMetadataPrefix(final String metadataPrefix) { - this.metadataPrefix = metadataPrefix; - } - - /** - * Sets the OAI-PM set specification . - * - * @param setSpec th OAI-PM set specification - */ - public void setSetSpec(final String setSpec) { - this.setSpec = setSpec; - } - - @Override - public void process(final String baseUrl) { - - try { - RawWrite.run(baseUrl, this.dateFrom, this.dateUntil, this.metadataPrefix, - this.setSpec, OUTPUT_STREAM); - } catch (IOException e) { - e.printStackTrace(); - } catch (ParserConfigurationException e) { - e.printStackTrace(); - } catch (SAXException e) { - e.printStackTrace(); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (XPathException e) { - e.printStackTrace(); - } - try { - getReceiver().process( - new InputStreamReader(new ByteArrayInputStream(OUTPUT_STREAM - .toByteArray()), encoding)); - } catch (IOException e) { - throw new MetafactureException(e); - } - } +public final class OaiPmhOpener extends DefaultObjectPipe> { + + private String encoding = "UTF-8"; + + private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + private String dateFrom; + + private String dateUntil; + + private String setSpec; + + private String metadataPrefix; + + /** + * Default constructor + */ + public OaiPmhOpener() { + } + + /** + * Sets the encoding to use. The default setting is UTF-8. + * + * @param encoding new default encoding + */ + public void setEncoding(final String encoding) { + this.encoding = encoding; + } + + /** + * Sets the beginning of the retrieving of updated data. The form is + * YYYY-MM-DD . + * + * @param dateFrom The form is YYYY-MM-DD . + */ + public void setDateFrom(final String dateFrom) { + this.dateFrom = dateFrom; + } + + /** + * Sets the end of the retrieving of updated data. The form is YYYY-MM-DD . + * + * @param dateUntil The form is YYYY-MM-DD . + */ + public void setDateUntil(final String dateUntil) { + this.dateUntil = dateUntil; + } + + /** + * Sets the OAI-PM metadata prefix . + * + * @param metadataPrefix the OAI-PM metadata prefix + */ + public void setMetadataPrefix(final String metadataPrefix) { + this.metadataPrefix = metadataPrefix; + } + + /** + * Sets the OAI-PM set specification . + * + * @param setSpec th OAI-PM set specification + */ + public void setSetSpec(final String setSpec) { + this.setSpec = setSpec; + } + + @Override + public void process(final String baseUrl) { + + try { + RawWrite.run(baseUrl, this.dateFrom, this.dateUntil, this.metadataPrefix, this.setSpec, outputStream); + } + catch (final IOException e) { + e.printStackTrace(); + } + catch (final ParserConfigurationException e) { + e.printStackTrace(); + } + catch (final SAXException e) { + e.printStackTrace(); + } + catch (final NoSuchFieldException e) { + e.printStackTrace(); + } + catch (XPathException e) { + e.printStackTrace(); + } + try { + getReceiver().process( + new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray()), encoding)); + } + catch (final IOException e) { + throw new MetafactureException(e); + } + } } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/DirectoryBuilder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/DirectoryBuilder.java index f168e44fc..1ea6deb66 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/DirectoryBuilder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/DirectoryBuilder.java @@ -13,11 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.iso2709; -import static org.metafacture.biblio.iso2709.Iso2709Constants.FIELD_SEPARATOR; -import static org.metafacture.biblio.iso2709.Iso2709Constants.MAX_PAYLOAD_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.TAG_LENGTH; +package org.metafacture.biblio.iso2709; import org.metafacture.framework.FormatException; @@ -31,6 +28,8 @@ */ final class DirectoryBuilder { + private static final int RADIX = 10; + private final Iso646ByteBuffer buffer; private final int fieldStartLength; @@ -41,11 +40,11 @@ final class DirectoryBuilder { private final int maxFieldLength; DirectoryBuilder(final RecordFormat format) { - buffer = new Iso646ByteBuffer(MAX_PAYLOAD_LENGTH); + buffer = new Iso646ByteBuffer(Iso2709Constants.MAX_PAYLOAD_LENGTH); fieldStartLength = format.getFieldStartLength(); fieldLengthLength = format.getFieldLengthLength(); implDefinedPartLength = format.getImplDefinedPartLength(); - entryLength = TAG_LENGTH + fieldStartLength + fieldLengthLength + + entryLength = Iso2709Constants.TAG_LENGTH + fieldStartLength + fieldLengthLength + implDefinedPartLength; maxFieldStart = calculateMaxValue(fieldStartLength); maxFieldLength = calculateMaxValue(fieldLengthLength); @@ -54,15 +53,15 @@ final class DirectoryBuilder { private int calculateMaxValue(final int digits) { assert digits >= 0; int maxValue = 1; - for (int i = 0; i < digits; i++) { - maxValue *= 10; + for (int i = 0; i < digits; ++i) { + maxValue *= RADIX; } return maxValue - 1; } void addEntries(final char[] tag, final char[] implDefinedPart, final int fieldStart, final int fieldEnd) { - assert tag.length == TAG_LENGTH; + assert tag.length == Iso2709Constants.TAG_LENGTH; assert implDefinedPart.length == implDefinedPartLength; assert fieldStart >= 0; assert fieldEnd >= fieldStart; @@ -125,7 +124,7 @@ void copyToBuffer(final byte[] destBuffer, final int fromIndex) { System.arraycopy(buffer.getByteArray(), 0, destBuffer, fromIndex, directoryLength); final int directoryEnd = fromIndex + directoryLength; - destBuffer[directoryEnd] = FIELD_SEPARATOR; + destBuffer[directoryEnd] = Iso2709Constants.FIELD_SEPARATOR; } @Override diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/DirectoryEntry.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/DirectoryEntry.java index 99709d964..b6ff9b1ed 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/DirectoryEntry.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/DirectoryEntry.java @@ -13,12 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.iso2709; -import static org.metafacture.biblio.iso2709.Iso2709Constants.MAX_BASE_ADDRESS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.MIN_BASE_ADDRESS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RECORD_LABEL_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.TAG_LENGTH; +package org.metafacture.biblio.iso2709; /** * Provides access to a directory entry. A {@code DirectoryEntry} works like @@ -43,21 +39,21 @@ class DirectoryEntry { DirectoryEntry(final Iso646ByteBuffer buffer, final RecordFormat recordFormat, final int baseAddress) { assert buffer != null; - assert baseAddress >= MIN_BASE_ADDRESS; - assert baseAddress <= MAX_BASE_ADDRESS; + assert baseAddress >= Iso2709Constants.MIN_BASE_ADDRESS; + assert baseAddress <= Iso2709Constants.MAX_BASE_ADDRESS; this.buffer = buffer; directoryEnd = baseAddress - Byte.BYTES; fieldLengthLength = recordFormat.getFieldLengthLength(); fieldStartLength = recordFormat.getFieldStartLength(); implDefinedPartLength = recordFormat.getImplDefinedPartLength(); - entryLength = TAG_LENGTH + fieldLengthLength + fieldStartLength + + entryLength = Iso2709Constants.TAG_LENGTH + fieldLengthLength + fieldStartLength + implDefinedPartLength; rewind(); } void rewind() { - currentPosition = RECORD_LABEL_LENGTH; + currentPosition = Iso2709Constants.RECORD_LABEL_LENGTH; } void gotoNext() { @@ -71,25 +67,25 @@ boolean endOfDirectoryReached() { char[] getTag() { assert currentPosition < directoryEnd; - return buffer.charsAt(currentPosition, TAG_LENGTH); + return buffer.charsAt(currentPosition, Iso2709Constants.TAG_LENGTH); } int getFieldLength() { assert currentPosition < directoryEnd; - final int fieldLengthStart = currentPosition + TAG_LENGTH; + final int fieldLengthStart = currentPosition + Iso2709Constants.TAG_LENGTH; return buffer.parseIntAt(fieldLengthStart, fieldLengthLength); } int getFieldStart() { assert currentPosition < directoryEnd; - final int fieldStartStart = currentPosition + TAG_LENGTH + + final int fieldStartStart = currentPosition + Iso2709Constants.TAG_LENGTH + fieldLengthLength; return buffer.parseIntAt(fieldStartStart, fieldStartLength); } char[] getImplDefinedPart() { assert currentPosition < directoryEnd; - final int implDefinedPartStart = currentPosition + TAG_LENGTH + + final int implDefinedPartStart = currentPosition + Iso2709Constants.TAG_LENGTH + fieldLengthLength + fieldStartLength; return buffer.charsAt(implDefinedPartStart, implDefinedPartLength); } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/FieldHandler.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/FieldHandler.java index 59ffb4787..785aa9bfa 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/FieldHandler.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/FieldHandler.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; /** @@ -23,14 +24,42 @@ */ public interface FieldHandler { + /** + * Reference a field. + * + * @param tag the tag + * @param implDefinedPart the impl defined part + * @param value the value + */ void referenceField(char[] tag, char[] implDefinedPart, String value); + /** + * Starts a data field. + * + * @param tag the tag + * @param implDefinedPart the impl defined part + * @param indicators the indicators + */ void startDataField(char[] tag, char[] implDefinedPart, char[] indicators); + /** + * Ends the data field. + */ void endDataField(); + /** + * Sets the impl defined part. + * + * @param implDefinedPart he impl defined part + */ void additionalImplDefinedPart(char[] implDefinedPart); + /** + * Sets the identifier to a value. + * + * @param identifier the identifier + * @param value the value + */ void data(char[] identifier, String value); } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/FieldsBuilder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/FieldsBuilder.java index a5ac854fe..038a87f80 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/FieldsBuilder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/FieldsBuilder.java @@ -13,17 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; -import static org.metafacture.biblio.iso2709.Iso2709Constants.FIELD_SEPARATOR; -import static org.metafacture.biblio.iso2709.Iso2709Constants.IDENTIFIER_MARKER; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RECORD_SEPARATOR; +import org.metafacture.framework.FormatException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import org.metafacture.framework.FormatException; - /** * Builds a list of fields in ISO 2709:2008 format. * @@ -43,7 +40,6 @@ final class FieldsBuilder { private int undoMarker = NO_MARKER_SET; private boolean inField; - FieldsBuilder(final RecordFormat format, final int maxSize) { buffer = new Iso646ByteBuffer(maxSize); identifierLength = format.getIdentifierLength(); @@ -75,7 +71,7 @@ int endField() { assert inField; checkCapacity(Byte.BYTES); inField = false; - buffer.writeByte(FIELD_SEPARATOR); + buffer.writeByte(Iso2709Constants.FIELD_SEPARATOR); return buffer.getWritePosition(); } @@ -91,7 +87,7 @@ void appendSubfield(final char[] identifier, final String value) { final byte[] bytes = value.getBytes(charset); checkCapacity(bytes.length + identifierLength + Byte.BYTES); if (identifierLength > 0) { - buffer.writeByte(IDENTIFIER_MARKER); + buffer.writeByte(Iso2709Constants.IDENTIFIER_MARKER); buffer.writeChars(identifier); } buffer.writeBytes(bytes); @@ -126,7 +122,7 @@ void copyToBuffer(final byte[] destBuffer, final int fromIndex) { System.arraycopy(buffer.getByteArray(), 0, destBuffer, fromIndex, fieldLength); final int fieldsEnd = fromIndex + fieldLength; - destBuffer[fieldsEnd] = RECORD_SEPARATOR; + destBuffer[fieldsEnd] = Iso2709Constants.RECORD_SEPARATOR; } @Override diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso2709Constants.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso2709Constants.java index 8ed53f989..ebe9475c8 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso2709Constants.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso2709Constants.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; /** diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso646ByteBuffer.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso646ByteBuffer.java index 3586f96dd..ed603257e 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso646ByteBuffer.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso646ByteBuffer.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.iso2709; -import java.nio.charset.Charset; +package org.metafacture.biblio.iso2709; import org.metafacture.framework.FormatException; +import java.nio.charset.Charset; + /** * Provides methods for reading and writing strings and integers in a byte * array. @@ -255,7 +256,7 @@ void writeBytes(final byte[] array) { } void writeInt(final int value) { - assert 0 <= value && value < 10; + assert 0 <= value && value < RADIX; byteArray[writePosition] = (byte) (Iso646Constants.ZERO + value); writePosition += 1; } @@ -265,7 +266,7 @@ void writeInt(final int value, final int digits) { assert digits >= 0; assert (writePosition + digits) <= byteArray.length; int head = value; - for (int i = writePosition + digits - 1; i >= writePosition; i--) { + for (int i = writePosition + digits - 1; i >= writePosition; --i) { byteArray[i] = (byte) (Iso646Constants.ZERO + head % RADIX); head /= RADIX; } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso646Constants.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso646Constants.java index f90794bd9..eee706496 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso646Constants.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Iso646Constants.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; import java.nio.charset.Charset; diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Label.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Label.java index 166fa36c7..6068eee5b 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Label.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Label.java @@ -13,27 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.iso2709; -import static org.metafacture.biblio.iso2709.Iso2709Constants.BASE_ADDRESS_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.BASE_ADDRESS_START; -import static org.metafacture.biblio.iso2709.Iso2709Constants.FIELD_LENGTH_LENGTH_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.FIELD_START_LENGTH_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.IDENTIFIER_LENGTH_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.IMPL_CODES_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.IMPL_CODES_START; -import static org.metafacture.biblio.iso2709.Iso2709Constants.IMPL_DEFINED_PART_LENGTH_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.INDICATOR_LENGTH_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RECORD_LABEL_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RECORD_LENGTH_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RECORD_LENGTH_START; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RECORD_STATUS_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RESERVED_CHAR_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.SYSTEM_CHARS_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.SYSTEM_CHARS_START; +package org.metafacture.biblio.iso2709; /** - * Provides read access to the record label of a ISO 2709:2008 formatted + * Provides read access to the record label of an ISO 2709:2008 formatted * record. The record label consists of the first 24 octets of the record. *

* Use {@link LabelBuilder} if write access to the label is required. @@ -46,7 +30,7 @@ class Label { Label(final Iso646ByteBuffer buffer) { final int bufferLength = buffer.getLength(); - assert bufferLength >= RECORD_LABEL_LENGTH; + assert bufferLength >= Iso2709Constants.RECORD_LABEL_LENGTH; this.buffer = buffer; } @@ -61,52 +45,52 @@ RecordFormat getRecordFormat() { } int getRecordLength() { - return buffer.parseIntAt(RECORD_LENGTH_START, RECORD_LENGTH_LENGTH); + return buffer.parseIntAt(Iso2709Constants.RECORD_LENGTH_START, Iso2709Constants.RECORD_LENGTH_LENGTH); } char getRecordStatus() { - return buffer.charAt(RECORD_STATUS_POS); + return buffer.charAt(Iso2709Constants.RECORD_STATUS_POS); } char[] getImplCodes() { - return buffer.charsAt(IMPL_CODES_START, IMPL_CODES_LENGTH); + return buffer.charsAt(Iso2709Constants.IMPL_CODES_START, Iso2709Constants.IMPL_CODES_LENGTH); } int getIndicatorLength() { - return buffer.parseIntAt(INDICATOR_LENGTH_POS); + return buffer.parseIntAt(Iso2709Constants.INDICATOR_LENGTH_POS); } int getIdentifierLength() { - return buffer.parseIntAt(IDENTIFIER_LENGTH_POS); + return buffer.parseIntAt(Iso2709Constants.IDENTIFIER_LENGTH_POS); } int getBaseAddress() { - return buffer.parseIntAt(BASE_ADDRESS_START, BASE_ADDRESS_LENGTH); + return buffer.parseIntAt(Iso2709Constants.BASE_ADDRESS_START, Iso2709Constants.BASE_ADDRESS_LENGTH); } char[] getSystemChars() { - return buffer.charsAt(SYSTEM_CHARS_START, SYSTEM_CHARS_LENGTH); + return buffer.charsAt(Iso2709Constants.SYSTEM_CHARS_START, Iso2709Constants.SYSTEM_CHARS_LENGTH); } int getFieldLengthLength() { - return buffer.parseIntAt(FIELD_LENGTH_LENGTH_POS); + return buffer.parseIntAt(Iso2709Constants.FIELD_LENGTH_LENGTH_POS); } int getFieldStartLength() { - return buffer.parseIntAt(FIELD_START_LENGTH_POS); + return buffer.parseIntAt(Iso2709Constants.FIELD_START_LENGTH_POS); } int getImplDefinedPartLength() { - return buffer.parseIntAt(IMPL_DEFINED_PART_LENGTH_POS); + return buffer.parseIntAt(Iso2709Constants.IMPL_DEFINED_PART_LENGTH_POS); } char getReservedChar() { - return buffer.charAt(RESERVED_CHAR_POS); + return buffer.charAt(Iso2709Constants.RESERVED_CHAR_POS); } @Override public String toString() { - return buffer.stringAt(0, RECORD_LABEL_LENGTH, Iso646Constants.CHARSET); + return buffer.stringAt(0, Iso2709Constants.RECORD_LABEL_LENGTH, Iso646Constants.CHARSET); } } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/LabelBuilder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/LabelBuilder.java index f8572fdca..270d59f66 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/LabelBuilder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/LabelBuilder.java @@ -13,28 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.iso2709; -import static org.metafacture.biblio.iso2709.Iso2709Constants.BASE_ADDRESS_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.BASE_ADDRESS_START; -import static org.metafacture.biblio.iso2709.Iso2709Constants.FIELD_LENGTH_LENGTH_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.FIELD_START_LENGTH_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.IDENTIFIER_LENGTH_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.IMPL_CODES_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.IMPL_CODES_START; -import static org.metafacture.biblio.iso2709.Iso2709Constants.IMPL_DEFINED_PART_LENGTH_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.INDICATOR_LENGTH_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.MAX_BASE_ADDRESS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.MAX_RECORD_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.MIN_BASE_ADDRESS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.MIN_RECORD_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RECORD_LABEL_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RECORD_LENGTH_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RECORD_LENGTH_START; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RECORD_STATUS_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RESERVED_CHAR_POS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.SYSTEM_CHARS_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.SYSTEM_CHARS_START; +package org.metafacture.biblio.iso2709; import java.util.Arrays; @@ -49,87 +29,87 @@ class LabelBuilder { private static final char DEFAULT_RECORD_STATUS = ' '; - private static final char[] DEFAULT_IMPL_CODES = { ' ', ' ', ' ', ' ' }; - private static final char[] DEFAULT_SYSTEM_CHARS = { ' ', ' ', ' ' }; + private static final char[] DEFAULT_IMPL_CODES = {' ', ' ', ' ', ' '}; + private static final char[] DEFAULT_SYSTEM_CHARS = {' ', ' ', ' '}; private static final char DEFAULT_RESERVED_CHAR = ' '; private final Iso646ByteBuffer buffer; private final byte[] defaultLabel; LabelBuilder(final RecordFormat recordFormat) { - buffer = new Iso646ByteBuffer(RECORD_LABEL_LENGTH); + buffer = new Iso646ByteBuffer(Iso2709Constants.RECORD_LABEL_LENGTH); defaultLabel = buildDefaultLabel(recordFormat); } private byte[] buildDefaultLabel(final RecordFormat recordFormat) { writeRecordFormatToLabel(recordFormat); - setRecordLength(MIN_RECORD_LENGTH); + setRecordLength(Iso2709Constants.MIN_RECORD_LENGTH); setRecordStatus(DEFAULT_RECORD_STATUS); setImplCodes(DEFAULT_IMPL_CODES); - setBaseAddress(MIN_BASE_ADDRESS); + setBaseAddress(Iso2709Constants.MIN_BASE_ADDRESS); setSystemChars(DEFAULT_SYSTEM_CHARS); setReservedChar(DEFAULT_RESERVED_CHAR); return Arrays.copyOf(buffer.getByteArray(), buffer.getLength()); } private void writeRecordFormatToLabel(final RecordFormat recordFormat) { - buffer.setWritePosition(INDICATOR_LENGTH_POS); + buffer.setWritePosition(Iso2709Constants.INDICATOR_LENGTH_POS); buffer.writeInt(recordFormat.getIndicatorLength()); - buffer.setWritePosition(IDENTIFIER_LENGTH_POS); + buffer.setWritePosition(Iso2709Constants.IDENTIFIER_LENGTH_POS); buffer.writeInt(recordFormat.getIdentifierLength()); - buffer.setWritePosition(FIELD_LENGTH_LENGTH_POS); + buffer.setWritePosition(Iso2709Constants.FIELD_LENGTH_LENGTH_POS); buffer.writeInt(recordFormat.getFieldLengthLength()); - buffer.setWritePosition(FIELD_START_LENGTH_POS); + buffer.setWritePosition(Iso2709Constants.FIELD_START_LENGTH_POS); buffer.writeInt(recordFormat.getFieldStartLength()); - buffer.setWritePosition(IMPL_DEFINED_PART_LENGTH_POS); + buffer.setWritePosition(Iso2709Constants.IMPL_DEFINED_PART_LENGTH_POS); buffer.writeInt(recordFormat.getImplDefinedPartLength()); } void setRecordLength(final int recordLength) { - assert recordLength >= MIN_RECORD_LENGTH; - assert recordLength <= MAX_RECORD_LENGTH; - buffer.setWritePosition(RECORD_LENGTH_START); - buffer.writeInt(recordLength, RECORD_LENGTH_LENGTH); + assert recordLength >= Iso2709Constants.MIN_RECORD_LENGTH; + assert recordLength <= Iso2709Constants.MAX_RECORD_LENGTH; + buffer.setWritePosition(Iso2709Constants.RECORD_LENGTH_START); + buffer.writeInt(recordLength, Iso2709Constants.RECORD_LENGTH_LENGTH); } void setRecordStatus(final char recordStatus) { - buffer.setWritePosition(RECORD_STATUS_POS); + buffer.setWritePosition(Iso2709Constants.RECORD_STATUS_POS); buffer.writeChar(recordStatus); } void setImplCodes(final char[] implCodes) { - assert implCodes.length == IMPL_CODES_LENGTH; - buffer.setWritePosition(IMPL_CODES_START); + assert implCodes.length == Iso2709Constants.IMPL_CODES_LENGTH; + buffer.setWritePosition(Iso2709Constants.IMPL_CODES_START); buffer.writeChars(implCodes); } void setImplCode(final int index, final char value) { - assert 0 <= index && index < IMPL_CODES_LENGTH; - buffer.setWritePosition(IMPL_CODES_START + index); + assert 0 <= index && index < Iso2709Constants.IMPL_CODES_LENGTH; + buffer.setWritePosition(Iso2709Constants.IMPL_CODES_START + index); buffer.writeChar(value); } void setBaseAddress(final int baseAddress) { - assert baseAddress >= MIN_BASE_ADDRESS; - assert baseAddress <= MAX_BASE_ADDRESS; - buffer.setWritePosition(BASE_ADDRESS_START); - buffer.writeInt(baseAddress, BASE_ADDRESS_LENGTH); + assert baseAddress >= Iso2709Constants.MIN_BASE_ADDRESS; + assert baseAddress <= Iso2709Constants.MAX_BASE_ADDRESS; + buffer.setWritePosition(Iso2709Constants.BASE_ADDRESS_START); + buffer.writeInt(baseAddress, Iso2709Constants.BASE_ADDRESS_LENGTH); } void setSystemChars(final char[] systemChars) { - assert systemChars.length == SYSTEM_CHARS_LENGTH; - buffer.setWritePosition(SYSTEM_CHARS_START); + assert systemChars.length == Iso2709Constants.SYSTEM_CHARS_LENGTH; + buffer.setWritePosition(Iso2709Constants.SYSTEM_CHARS_START); buffer.writeChars(systemChars); } void setSystemChar(final int index, final char value) { - assert 0 <= index && index < SYSTEM_CHARS_LENGTH; - buffer.setWritePosition(SYSTEM_CHARS_START + index); + assert 0 <= index && index < Iso2709Constants.SYSTEM_CHARS_LENGTH; + buffer.setWritePosition(Iso2709Constants.SYSTEM_CHARS_START + index); buffer.writeChar(value); } void setReservedChar(final char reservedChar) { - buffer.setWritePosition(RESERVED_CHAR_POS); + buffer.setWritePosition(Iso2709Constants.RESERVED_CHAR_POS); buffer.writeChar(reservedChar); } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Record.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Record.java index 38ba469be..2fcb8241f 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Record.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/Record.java @@ -13,19 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; -import static org.metafacture.biblio.iso2709.Iso2709Constants.FIELD_SEPARATOR; -import static org.metafacture.biblio.iso2709.Iso2709Constants.IDENTIFIER_MARKER; -import static org.metafacture.biblio.iso2709.Iso2709Constants.MIN_BASE_ADDRESS; -import static org.metafacture.biblio.iso2709.Iso2709Constants.MIN_RECORD_LENGTH; +import org.metafacture.commons.Require; +import org.metafacture.framework.FormatException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import org.metafacture.commons.Require; -import org.metafacture.framework.FormatException; - /** * Reads a record in ISO 2709:2008 format from a byte array. * @@ -36,7 +32,7 @@ public final class Record { private static final int RECORD_ID_MISSING = -1; private static final char[] EMPTY_IDENTIFIER = new char[0]; private static final byte[] DATA_SEPARATORS = { - FIELD_SEPARATOR, IDENTIFIER_MARKER + Iso2709Constants.FIELD_SEPARATOR, Iso2709Constants.IDENTIFIER_MARKER }; private final Iso646ByteBuffer buffer; @@ -73,13 +69,13 @@ public Record(final byte[] recordData) { } private void checkRecordDataLength(final byte[] recordData) { - if (recordData.length < MIN_RECORD_LENGTH) { + if (recordData.length < Iso2709Constants.MIN_RECORD_LENGTH) { throw new FormatException("record is too short"); } } private void checkBaseAddress() { - if (baseAddress < MIN_BASE_ADDRESS || baseAddress > buffer.getLength() - 1) { + if (baseAddress < Iso2709Constants.MIN_BASE_ADDRESS || baseAddress > buffer.getLength() - 1) { throw new FormatException("base address is out of range"); } } @@ -95,22 +91,47 @@ private int findRecordIdFieldStart() { return RECORD_ID_MISSING; } + /** + * Gets the record format of the Label. + * + * @return the record format of the Label + */ public RecordFormat getRecordFormat() { return label.getRecordFormat(); } + /** + * Gets the record status of the Label. + * + * @return the record status of the Label + */ public char getRecordStatus() { return label.getRecordStatus(); } + /** + * Gets the impl codes. + * + * @return the impl codes + */ public char[] getImplCodes() { return label.getImplCodes(); } + /** + * Gets the systems chars of the Label. + * + * @return the system chars + */ public char[] getSystemChars() { return label.getSystemChars(); } + /** + * Gets the reserved char of the Label. + * + * @return the reserved char + */ public char getReservedChar() { return label.getReservedChar(); } @@ -164,40 +185,43 @@ public String getRecordId() { public String getLabel() { return label.toString(); } + /** * Iterates through all fields in the record and calls the appropriate method * on the supplied {@link FieldHandler} instance. * - * @param fieldHandler instance of field handler. Must not be null. + * @param currentFieldHandler instance of field handler. Must not be null. */ - public void processFields(final FieldHandler fieldHandler) { - this.fieldHandler = Require.notNull(fieldHandler); + public void processFields(final FieldHandler currentFieldHandler) { + fieldHandler = Require.notNull(currentFieldHandler); boolean continuedField = false; directoryEntry.rewind(); while (!directoryEntry.endOfDirectoryReached()) { if (continuedField) { fieldHandler.additionalImplDefinedPart( directoryEntry.getImplDefinedPart()); - } else { + } + else { processField(); } continuedField = directoryEntry.isContinuedField(); directoryEntry.gotoNext(); } - this.fieldHandler = null; + fieldHandler = null; } private void processField() { if (directoryEntry.isReferenceField()) { processReferenceField(); - } else { + } + else { processDataField(); } } private void processReferenceField() { final int fieldStart = baseAddress + directoryEntry.getFieldStart(); - final int fieldLength = buffer.distanceTo(FIELD_SEPARATOR, fieldStart); + final int fieldLength = buffer.distanceTo(Iso2709Constants.FIELD_SEPARATOR, fieldStart); final String value = buffer.stringAt(fieldStart, fieldLength, charset); fieldHandler.referenceField(directoryEntry.getTag(), directoryEntry.getImplDefinedPart(), value); @@ -214,7 +238,7 @@ private void processDataField() { private void processDataValues(final int fromIndex) { int start = fromIndex; - while (buffer.byteAt(start) != FIELD_SEPARATOR) { + while (buffer.byteAt(start) != Iso2709Constants.FIELD_SEPARATOR) { start = processDataValue(start); } } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/RecordBuilder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/RecordBuilder.java index f4b9deb54..22d13b260 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/RecordBuilder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/RecordBuilder.java @@ -13,21 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; -import static org.metafacture.biblio.iso2709.Iso2709Constants.IMPL_CODES_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.MAX_PAYLOAD_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.RECORD_LABEL_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.SYSTEM_CHARS_LENGTH; -import static org.metafacture.biblio.iso2709.Iso2709Constants.TAG_LENGTH; +import org.metafacture.commons.Require; +import org.metafacture.framework.FormatException; import java.nio.charset.Charset; import java.util.Arrays; import java.util.regex.Pattern; -import org.metafacture.commons.Require; -import org.metafacture.framework.FormatException; - /** * Builds records in ISO2709:2008 format. * @@ -37,7 +32,7 @@ public final class RecordBuilder { private static final char[] EMPTY_IDENTIFIER = new char[0]; - private static final char[] ID_FIELD_TAG = { '0', '0', '1' }; + private static final char[] ID_FIELD_TAG = {'0', '0', '1'}; private static final Pattern REFERENCE_FIELD_TAG_PATTERN = Pattern.compile( "^00[1-9a-zA-Z]$"); private static final Pattern DATA_FIELD_TAG_PATTERN = Pattern.compile( @@ -55,17 +50,23 @@ public final class RecordBuilder { private final char[] defaultIndicators; private final char[] defaultIdentifier; - private final char[] tag = new char[TAG_LENGTH]; + private final char[] tag = new char[Iso2709Constants.TAG_LENGTH]; private final char[] implDefinedPart; private AppendState appendState; private int fieldStart; + /** + * Initializes the RecordBuilder based on a RecordFormat. + * + * @param recordFormat RecordFormat as base to configure the RecordBuilder + * @see RecordFormat + */ public RecordBuilder(final RecordFormat recordFormat) { Require.notNull(recordFormat); label = new LabelBuilder(recordFormat); directory = new DirectoryBuilder(recordFormat); - fields = new FieldsBuilder(recordFormat, MAX_PAYLOAD_LENGTH); + fields = new FieldsBuilder(recordFormat, Iso2709Constants.MAX_PAYLOAD_LENGTH); indicatorLength = recordFormat.getIndicatorLength(); identifierLength = recordFormat.getIdentifierLength(); implDefinedPartLength = recordFormat.getImplDefinedPartLength(); @@ -73,7 +74,8 @@ public RecordBuilder(final RecordFormat recordFormat) { defaultIndicators = arrayOfNSpaceChars(indicatorLength); if (identifierLength > 1) { defaultIdentifier = arrayOfNSpaceChars(identifierLength - 1); - } else { + } + else { defaultIdentifier = EMPTY_IDENTIFIER; } implDefinedPart = new char[implDefinedPartLength]; @@ -86,126 +88,226 @@ private char[] arrayOfNSpaceChars(final int count) { return chars; } + /** + * Sets the charset of the FieldsBuilder. + * + * @param charset Charset used by the FieldsBuilder + * @see FieldsBuilder + * @see Charset + */ public void setCharset(final Charset charset) { fields.setCharset(Require.notNull(charset)); } + /** + * Gets the Charset of the FieldsBuilder. + * + * @return the Charset + */ public Charset getCharset() { return fields.getCharset(); } + /** + * Sets the record status of the LabelBuilder. + * + * @param recordStatus 7 bit char record status + * @see Iso2709Constants#RECORD_STATUS_POS + * @see LabelBuilder + */ public void setRecordStatus(final char recordStatus) { require7BitAscii(recordStatus); label.setRecordStatus(recordStatus); } + /** + * Sets the impl codes in the LabelBuilder. + * + * @param implCodes char array of 7 bit impl codes + * @see Iso2709Constants + * @see LabelBuilder + */ public void setImplCodes(final char[] implCodes) { Require.notNull(implCodes); - Require.that(implCodes.length == IMPL_CODES_LENGTH); + Require.that(implCodes.length == Iso2709Constants.IMPL_CODES_LENGTH); require7BitAscii(implCodes); label.setImplCodes(implCodes); } + /** + * Sets an impl code at a given position in the LabelBuilder. + * + * @param index index of the 7 bit impl code + * @param implCode char of a 7 bit impl code + * @see Iso2709Constants + * @see LabelBuilder + */ public void setImplCode(final int index, final char implCode) { - Require.that(0 <= index && index < IMPL_CODES_LENGTH); + Require.that(0 <= index && index < Iso2709Constants.IMPL_CODES_LENGTH); require7BitAscii(implCode); label.setImplCode(index, implCode); } + /** + * Sets the system chars in the LabelBuilder. + * + * @param systemChars 7-bit char array to be set as system chars + * @see Iso2709Constants + * @see LabelBuilder + */ public void setSystemChars(final char[] systemChars) { Require.notNull(systemChars); - Require.that(systemChars.length == SYSTEM_CHARS_LENGTH); + Require.that(systemChars.length == Iso2709Constants.SYSTEM_CHARS_LENGTH); require7BitAscii(systemChars); label.setSystemChars(systemChars); } + /** + * Sets a system char at the given position in the LabelBuilder. + * + * @param systemChar 7-bit char to be set as the system char + * @param index position of the system char + * @see Iso2709Constants + * @see LabelBuilder + */ public void setSystemChar(final int index, final char systemChar) { - Require.that(0 <= index && index < SYSTEM_CHARS_LENGTH); + Require.that(0 <= index && index < Iso2709Constants.SYSTEM_CHARS_LENGTH); require7BitAscii(systemChar); label.setSystemChar(index, systemChar); } + /** + * Sets a reserved char in the LabelBuilder. + * + * @param reservedChar 7-bit char to be set as reserved char + * @see Iso2709Constants + * @see LabelBuilder + */ public void setReservedChar(final char reservedChar) { require7BitAscii(reservedChar); label.setReservedChar(reservedChar); } + /** + * Appends an identifier field. + * + * @param value String that is appended as an identfier field + */ public void appendIdentifierField(final String value) { - appendIdentifierField(defaultImplDefinedPart,value); + appendIdentifierField(defaultImplDefinedPart, value); } - public void appendIdentifierField(final char[] implDefinedPart, - final String value) { + /** + * Appends an identifier field in dependency of the current impl defined part. + * + * @param currentImplDefinedPart char array of the current impl defined part + * @param value String that is appended as an identfier field + */ + public void appendIdentifierField(final char[] currentImplDefinedPart, final String value) { requireNotInDataField(); requireNotAppendingReferenceFields(); requireNotAppendingDataFields(); if (appendState != AppendState.ID_FIELD) { throw new IllegalStateException("no id field allowed"); } - appendReferenceField(ID_FIELD_TAG, implDefinedPart, value); - } - - public void appendReferenceField(final char[] tag, final String value) { - appendReferenceField(tag, defaultImplDefinedPart, value); - } - - public void appendReferenceField(final char[] tag, - final char[] implDefinedPart, final String value) { + appendReferenceField(ID_FIELD_TAG, currentImplDefinedPart, value); + } + + /** + * Appends a reference field in dependency of the current tag and of the default + * impl defined part. + * + * @param currentTag char array of the current tag + * @param value String that is appended as a reference field + */ + public void appendReferenceField(final char[] currentTag, final String value) { + appendReferenceField(currentTag, defaultImplDefinedPart, value); + } + + /** + * Appends a reference field in dependency of the current tag and of the + * current impl defined part. + * + * @param currentTag char array of the current tag + * @param currentImplDefinedPart char array of the current impl defined part + * @param value String that is appended as a reference field + */ + public void appendReferenceField(final char[] currentTag, final char[] currentImplDefinedPart, final String value) { requireNotInDataField(); requireNotAppendingDataFields(); - Require.notNull(tag); - Require.notNull(implDefinedPart); - Require.that(implDefinedPart.length == implDefinedPartLength); - require7BitAscii(implDefinedPart); + Require.notNull(currentTag); + Require.notNull(currentImplDefinedPart); + Require.that(currentImplDefinedPart.length == implDefinedPartLength); + require7BitAscii(currentImplDefinedPart); Require.notNull(value); - checkValidReferenceFieldTag(tag); - final int fieldStart = fields.startField(); + checkValidReferenceFieldTag(currentTag); + final int currentFieldStart = fields.startField(); fields.appendValue(value); final int fieldEnd = fields.endField(); try { - directory.addEntries(tag, implDefinedPart, fieldStart, fieldEnd); - } catch (final FormatException e) { + directory.addEntries(currentTag, currentImplDefinedPart, currentFieldStart, fieldEnd); + } + catch (final FormatException e) { fields.undoLastField(); throw e; } appendState = AppendState.REFERENCE_FIELD; } - private void checkValidReferenceFieldTag(final char[] tag) { - if (!REFERENCE_FIELD_TAG_PATTERN.matcher(String.valueOf(tag)).matches()) { + private void checkValidReferenceFieldTag(final char[] currentTag) { + if (!REFERENCE_FIELD_TAG_PATTERN.matcher(String.valueOf(currentTag)).matches()) { throw new FormatException("invalid tag format for reference field"); } } - public void startDataField(final char[] tag) { - startDataField(tag, defaultIndicators); - } - - public void startDataField(final char[] tag, final char[] indicators) { - startDataField(tag, indicators, defaultImplDefinedPart); - } - - public void startDataField(final char[] tag, final char[] indicators, - final char[] implDefinedPart) { + /** + * Starts a data field in dependency of the current tag and default indicators. + * + * @param currentTag char array of the current tag + */ + public void startDataField(final char[] currentTag) { + startDataField(currentTag, defaultIndicators); + } + + /** + * Starts a data field in dependency of the current tag, indicators and of the + * default impl defined part. + * + * @param currentTag char array of the current tag + * @param indicators char array of the current indicators + */ + public void startDataField(final char[] currentTag, final char[] indicators) { + startDataField(currentTag, indicators, defaultImplDefinedPart); + } + + /** + * Starts a data field in dependency of the current tag, indicators and of the + * current impl defined part. + * + * @param currentTag char array of the current tag + * @param indicators char array of the current indicators + * @param currentImplDefinedPart char array of the current impl defined part + */ + public void startDataField(final char[] currentTag, final char[] indicators, final char[] currentImplDefinedPart) { requireNotInDataField(); - Require.notNull(tag); + Require.notNull(currentTag); Require.notNull(indicators); Require.that(indicators.length == indicatorLength); require7BitAscii(indicators); - Require.notNull(implDefinedPart); - Require.that(implDefinedPart.length == implDefinedPartLength); - require7BitAscii(implDefinedPart); + Require.notNull(currentImplDefinedPart); + Require.that(currentImplDefinedPart.length == implDefinedPartLength); + require7BitAscii(currentImplDefinedPart); - checkValidDataFieldTag(tag); - copyArray(tag, this.tag); - copyArray(implDefinedPart, this.implDefinedPart); + checkValidDataFieldTag(currentTag); + copyArray(currentTag, tag); + copyArray(currentImplDefinedPart, implDefinedPart); fieldStart = fields.startField(indicators); appendState = AppendState.IN_DATA_FIELD; } - private void checkValidDataFieldTag(final char[] tag) { - if (!DATA_FIELD_TAG_PATTERN.matcher(String.valueOf(tag)).matches()) { + private void checkValidDataFieldTag(final char[] currentTag) { + if (!DATA_FIELD_TAG_PATTERN.matcher(String.valueOf(currentTag)).matches()) { throw new FormatException("invalid tag format for data field"); } } @@ -214,24 +316,39 @@ private void copyArray(final char[] source, final char[] destination) { System.arraycopy(source, 0, destination, 0, destination.length); } + /** + * Ends a data field. + */ public void endDataField() { requireInDataField(); final int fieldEnd = fields.endField(); appendState = AppendState.DATA_FIELD; try { directory.addEntries(tag, implDefinedPart, fieldStart, fieldEnd); - } catch (final FormatException e) { + } + catch (final FormatException e) { fields.undoLastField(); throw e; } } + /** + * Appends a subfield. + * + * @param value String of the subfield to be appended + */ public void appendSubfield(final String value) { requireInDataField(); Require.notNull(value); fields.appendSubfield(defaultIdentifier, value); } + /** + * Appends a subfield in dependency of an identifier. + * + * @param identifier char array of an identifier + * @param value String of the subfield to be appended + */ public void appendSubfield(final char[] identifier, final String value) { requireInDataField(); Require.notNull(identifier); @@ -241,15 +358,20 @@ public void appendSubfield(final char[] identifier, final String value) { fields.appendSubfield(identifier, value); } + /** + * Builds the record. + * + * @return byte array of the record + */ public byte[] build() { requireNotInDataField(); - final int baseAddress = RECORD_LABEL_LENGTH + directory.length(); + final int baseAddress = Iso2709Constants.RECORD_LABEL_LENGTH + directory.length(); final int recordLength = baseAddress + fields.length(); label.setBaseAddress(baseAddress); label.setRecordLength(recordLength); final byte[] recordBuffer = new byte[recordLength]; label.copyToBuffer(recordBuffer); - directory.copyToBuffer(recordBuffer, RECORD_LABEL_LENGTH); + directory.copyToBuffer(recordBuffer, Iso2709Constants.RECORD_LABEL_LENGTH); fields.copyToBuffer(recordBuffer, baseAddress); return recordBuffer; } @@ -291,6 +413,10 @@ private void require7BitAscii(final char charCode) { Require.that(charCode != Iso646Constants.INFORMATION_SEPARATOR_3); } + /** + * Resets the label, directory and the fields. Sets the "append state" to "id + * field". + */ public void reset() { label.reset(); directory.reset(); @@ -300,9 +426,9 @@ public void reset() { @Override public String toString() { - return "label: " + label.toString() + "\n" - + "directory: " + directory.toString() + "\n" - + "fields: " + fields.toString(); + return "label: " + label.toString() + "\n" + + "directory: " + directory.toString() + "\n" + + "fields: " + fields.toString(); } private enum AppendState { diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/RecordFormat.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/RecordFormat.java index cd7ecb202..c302666df 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/RecordFormat.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/iso2709/RecordFormat.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.iso2709; -import java.util.Arrays; +package org.metafacture.biblio.iso2709; import org.metafacture.commons.Require; +import java.util.Arrays; + /** * Holds the configuration of an instance of the ISO2709:2008 format. * @@ -30,19 +31,19 @@ public final class RecordFormat { /** * The number of characters in the field tags. */ - public final int TAG_LENGTH = Iso2709Constants.TAG_LENGTH; + public static final int TAG_LENGTH = Iso2709Constants.TAG_LENGTH; /** * The number of characters in the implementation codes element of the * record leader. */ - public final int IMPL_CODES_LENGTH = Iso2709Constants.IMPL_CODES_LENGTH; + public static final int IMPL_CODES_LENGTH = Iso2709Constants.IMPL_CODES_LENGTH; /** * The number of characters in the system characters element of the * record leader. */ - public final int SYSTEM_CHARS_LENGTH = Iso2709Constants.SYSTEM_CHARS_LENGTH; + public static final int SYSTEM_CHARS_LENGTH = Iso2709Constants.SYSTEM_CHARS_LENGTH; private final int indicatorLength; private final int identifierLength; @@ -50,6 +51,15 @@ public final class RecordFormat { private final int fieldStartLength; private final int implDefinedPartLength; + /** + * Initializes the RecordFormat. + * + * @param indicatorLength the length of the indicator + * @param identifierLength the length of the identifier + * @param fieldLengthLength the length of the field length + * @param fieldStartLength the length of the field start + * @param implDefinedPartLength the length of the defined part + */ public RecordFormat(final int indicatorLength, final int identifierLength, final int fieldLengthLength, final int fieldStartLength, final int implDefinedPartLength) { @@ -60,6 +70,11 @@ public RecordFormat(final int indicatorLength, final int identifierLength, this.implDefinedPartLength = implDefinedPartLength; } + /** + * Initializes a RecordFormat defined by a RecordFormat. + * + * @param source the RecordFormat + */ public RecordFormat(final RecordFormat source) { Require.notNull(source); @@ -70,10 +85,21 @@ public RecordFormat(final RecordFormat source) { implDefinedPartLength = source.implDefinedPartLength; } + /** + * Returns a new default Builder. + * + * @return Builder + */ public static Builder create() { return new Builder(); } + /** + * Returns a new Builder created from a RecordFormat. + * + * @param source the RecordFormat + * @return Builder + */ public static Builder createFrom(final RecordFormat source) { return create() .withIndicatorLength(source.indicatorLength) @@ -83,22 +109,47 @@ public static Builder createFrom(final RecordFormat source) { .withImplDefinedPartLength(source.implDefinedPartLength); } + /** + * Gets the length of the indicator. + * + * @return the length of the indicator + */ public int getIndicatorLength() { return indicatorLength; } + /** + * Gets the length of the identifier. + * + * @return the length of the identifier + */ public int getIdentifierLength() { return identifierLength; } + /** + * Gets the length of the field length. + * + * @return the length of the field length + */ public int getFieldLengthLength() { return fieldLengthLength; } + /** + * Gets the length of the field start. + * + * @return length of the field start + */ public int getFieldStartLength() { return fieldStartLength; } + /** + * Gets the the length of the defined part. + * + * @return the length of the defined part + */ public int getImplDefinedPartLength() { return implDefinedPartLength; } @@ -110,19 +161,19 @@ public boolean equals(final Object obj) { } if (obj instanceof RecordFormat) { final RecordFormat other = (RecordFormat) obj; - return indicatorLength == other.indicatorLength - && identifierLength == other.identifierLength - && fieldLengthLength == other.fieldLengthLength - && fieldStartLength == other.fieldStartLength - && implDefinedPartLength == other.implDefinedPartLength; + return indicatorLength == other.indicatorLength && + identifierLength == other.identifierLength && + fieldLengthLength == other.fieldLengthLength && + fieldStartLength == other.fieldStartLength && + implDefinedPartLength == other.implDefinedPartLength; } return false; } @Override public int hashCode() { - final int[] items = { indicatorLength, identifierLength, fieldLengthLength, - fieldStartLength, implDefinedPartLength }; + final int[] items = {indicatorLength, identifierLength, fieldLengthLength, + fieldStartLength, implDefinedPartLength}; return Arrays.hashCode(items); } @@ -137,47 +188,87 @@ public String toString() { public static class Builder { + private static final int RADIX = 10; + private int indicatorLength; private int identifierLength; private int fieldLengthLength; private int fieldStartLength; private int implDefinedPartLength; - public Builder withIndicatorLength(final int indicatorLength) { - Require.notNegative(indicatorLength); - Require.that(indicatorLength <= 9); - this.indicatorLength = indicatorLength; + Builder() { + } + + /** + * Returns a new Builder with defined indicator length. + * + * @param currentIndicatorLength the length of the indicator + * @return Builder + */ + public Builder withIndicatorLength(final int currentIndicatorLength) { + Require.notNegative(currentIndicatorLength); + Require.that(currentIndicatorLength < RADIX); + indicatorLength = currentIndicatorLength; return this; } - public Builder withIdentifierLength(final int identifierLength) { - Require.notNegative(identifierLength); - Require.that(identifierLength <= 9); - this.identifierLength = identifierLength; + /** + * Returns a new Builder with defined identifier length. + * + * @param currentIdentifierLength the length of the identifier + * @return Builder + */ + public Builder withIdentifierLength(final int currentIdentifierLength) { + Require.notNegative(currentIdentifierLength); + Require.that(currentIdentifierLength < RADIX); + identifierLength = currentIdentifierLength; return this; } - public Builder withFieldLengthLength(final int fieldLengthLength) { - Require.that(fieldLengthLength > 0); - Require.that(fieldLengthLength <= 9); - this.fieldLengthLength = fieldLengthLength; + /** + * Returns a new Builder with defined field length length. + * + * @param currentFieldLengthLength the length of the field length + * @return Builder + */ + public Builder withFieldLengthLength(final int currentFieldLengthLength) { + Require.that(currentFieldLengthLength > 0); + Require.that(currentFieldLengthLength < RADIX); + fieldLengthLength = currentFieldLengthLength; return this; } - public Builder withFieldStartLength(final int fieldStartLength) { - Require.that(fieldStartLength > 0); - Require.that(fieldStartLength <= 9); - this.fieldStartLength = fieldStartLength; + /** + * Returns a new Builder with defined field start length. + * + * @param currentFieldStartLength the length of the field start + * @return Builder + */ + public Builder withFieldStartLength(final int currentFieldStartLength) { + Require.that(currentFieldStartLength > 0); + Require.that(currentFieldStartLength < RADIX); + fieldStartLength = currentFieldStartLength; return this; } - public Builder withImplDefinedPartLength(final int implDefinedPartLength) { - Require.notNegative(implDefinedPartLength); - Require.that(implDefinedPartLength <= 9); - this.implDefinedPartLength = implDefinedPartLength; + /** + * Returns a new Builder with defined part length. + * + * @param currentImplDefinedPartLength the length of the defined part + * @return Builder + */ + public Builder withImplDefinedPartLength(final int currentImplDefinedPartLength) { + Require.notNegative(currentImplDefinedPartLength); + Require.that(currentImplDefinedPartLength < RADIX); + implDefinedPartLength = currentImplDefinedPartLength; return this; } + /** + * Returns a new RecordFormat. + * + * @return RecordFormat + */ public RecordFormat build() { return new RecordFormat(indicatorLength, identifierLength, fieldLengthLength, fieldStartLength, implDefinedPartLength); diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Constants.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Constants.java index f729ff513..a8cd083ce 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Constants.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Constants.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.marc21; +import org.metafacture.biblio.iso2709.RecordFormat; + import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import org.metafacture.biblio.iso2709.RecordFormat; - /** * Useful constants for the MARC 21 format. * @@ -27,43 +28,43 @@ */ final class Marc21Constants { - static final RecordFormat MARC21_FORMAT = RecordFormat.create() - .withIndicatorLength(2) - .withIdentifierLength(2) - .withFieldLengthLength(4) - .withFieldStartLength(5) - .withImplDefinedPartLength(0) - .build(); + static final RecordFormat MARC21_FORMAT = RecordFormat.create() + .withIndicatorLength(2) + .withIdentifierLength(2) + .withFieldLengthLength(4) // checkstyle-disable-line MagicNumber + .withFieldStartLength(5) // checkstyle-disable-line MagicNumber + .withImplDefinedPartLength(0) + .build(); - static final Charset MARC21_CHARSET = StandardCharsets.UTF_8; + static final Charset MARC21_CHARSET = StandardCharsets.UTF_8; - static final int RECORD_TYPE_INDEX = 0; - static final int BIBLIOGRAPHIC_LEVEL_INDEX = 1; - static final int TYPE_OF_CONTROL_INDEX = 2; - static final int CHARACTER_CODING_INDEX = 3; - static final int ENCODING_LEVEL_INDEX = 0; - static final int CATALOGING_FORM_INDEX = 1; - static final int MULTIPART_LEVEL_INDEX = 2; + static final int RECORD_TYPE_INDEX = 0; + static final int BIBLIOGRAPHIC_LEVEL_INDEX = 1; + static final int TYPE_OF_CONTROL_INDEX = 2; + static final int CHARACTER_CODING_INDEX = 3; + static final int ENCODING_LEVEL_INDEX = 0; + static final int CATALOGING_FORM_INDEX = 1; + static final int MULTIPART_LEVEL_INDEX = 2; - static final char[] RECORD_STATUS_CODES = { 'a', 'c', 'd', 'n', 'p' }; - static final char[] RECORD_TYPE_CODES = { - 'a', 'c', 'd', 'e', 'f', 'g', 'i', 'j', 'k', 'm', 'o', 'p', 'r', 't' - }; - static final char[] BIBLIOGRAPHIC_LEVEL_CODES = { - 'a', 'b', 'c', 'd', 'i', 'm', 's' - }; - static final char[] TYPE_OF_CONTROL_CODES = { ' ', 'a' }; - static final char[] CHARACTER_CODING_CODES = { 'a' }; - static final char[] ENCODING_LEVEL_CODES = { - ' ', '1', '2', '3', '4', '5', '7', '8', 'u', 'z' - }; - static final char[] CATALOGING_FORM_CODES = { ' ', 'a', 'c', 'i', 'u' }; - static final char[] MULTIPART_LEVEL_CODES = { ' ', 'a', 'b', 'c' }; + static final char[] RECORD_STATUS_CODES = {'a', 'c', 'd', 'n', 'p'}; + static final char[] RECORD_TYPE_CODES = { + 'a', 'c', 'd', 'e', 'f', 'g', 'i', 'j', 'k', 'm', 'o', 'p', 'r', 't' + }; + static final char[] BIBLIOGRAPHIC_LEVEL_CODES = { + 'a', 'b', 'c', 'd', 'i', 'm', 's' + }; + static final char[] TYPE_OF_CONTROL_CODES = {' ', 'a'}; + static final char[] CHARACTER_CODING_CODES = {'a'}; + static final char[] ENCODING_LEVEL_CODES = { + ' ', '1', '2', '3', '4', '5', '7', '8', 'u', 'z' + }; + static final char[] CATALOGING_FORM_CODES = {' ', 'a', 'c', 'i', 'u'}; + static final char[] MULTIPART_LEVEL_CODES = {' ', 'a', 'b', 'c'}; - static final char RESERVED_CHAR = '0'; + static final char RESERVED_CHAR = '0'; - private Marc21Constants() { - throw new AssertionError("class should not be instantiated"); - } + private Marc21Constants() { + throw new AssertionError("class should not be instantiated"); + } } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Decoder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Decoder.java index e52b54e4e..c4697c661 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Decoder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Decoder.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.marc21; import org.metafacture.biblio.iso2709.FieldHandler; @@ -40,8 +41,9 @@ *

  • data fields. * * This decoder only supports MARC 21 records with UTF-8 encoding. Other - * character coding schemes are not supported. A {@link FormatException} is - * thrown if a record with an unsupported coding scheme is encountered. + * character coding schemes are not supported. A + * {@link org.metafacture.framework.FormatException} is thrown if a record + * with an unsupported coding scheme is encountered. *

    * The bibliographic information in the record leader is *

      @@ -55,18 +57,18 @@ *
    • multipart resource record level. *
    * This information is emitted as an entity named - * "{@value Marc21EventNames#LEADER_ENTITY}". It is emitted directly - * after the start-record event. The entity contains the following - * literals: + * {@value org.metafacture.biblio.marc21.Marc21EventNames#LEADER_ENTITY}. It is + * emitted directly after the start-record event. The entity contains + * the following literals: *
      - *
    1. {@value Marc21EventNames#RECORD_STATUS_LITERAL} - *
    2. {@value Marc21EventNames#RECORD_TYPE_LITERAL} - *
    3. {@value Marc21EventNames#BIBLIOGRAPHIC_LEVEL_LITERAL} - *
    4. {@value Marc21EventNames#TYPE_OF_CONTROL_LITERAL} - *
    5. {@value Marc21EventNames#CHARACTER_CODING_LITERAL} - *
    6. {@value Marc21EventNames#ENCODING_LEVEL_LITERAL} - *
    7. {@value Marc21EventNames#CATALOGING_FORM_LITERAL} - *
    8. {@value Marc21EventNames#MULTIPART_LEVEL_LITERAL} + *
    9. {@value org.metafacture.biblio.marc21.Marc21EventNames#RECORD_STATUS_LITERAL} + *
    10. {@value org.metafacture.biblio.marc21.Marc21EventNames#RECORD_TYPE_LITERAL} + *
    11. {@value org.metafacture.biblio.marc21.Marc21EventNames#BIBLIOGRAPHIC_LEVEL_LITERAL} + *
    12. {@value org.metafacture.biblio.marc21.Marc21EventNames#TYPE_OF_CONTROL_LITERAL} + *
    13. {@value org.metafacture.biblio.marc21.Marc21EventNames#CHARACTER_CODING_LITERAL} + *
    14. {@value org.metafacture.biblio.marc21.Marc21EventNames#ENCODING_LEVEL_LITERAL} + *
    15. {@value org.metafacture.biblio.marc21.Marc21EventNames#CATALOGING_FORM_LITERAL} + *
    16. {@value org.metafacture.biblio.marc21.Marc21EventNames#MULTIPART_LEVEL_LITERAL} *
    * The literals are emitted in the order in which they are listed here. The * values of these literals are the characters at the corresponding @@ -74,9 +76,9 @@ * MARC 21 * Standard: Record Leader for a description of the allowed values). The * literal values are always only single characters. As this decoder only - * supports MARC 21 records with UTF-8 encoding, the value of the literal - * "{@value Marc21EventNames#CHARACTER_CODING_LITERAL}" will - * always be "a". + * supports MARC 21 records with UTF-8 encoding, the value of the literal + * {@value org.metafacture.biblio.marc21.Marc21EventNames#CHARACTER_CODING_LITERAL} + * will always be "a". *

    * For example, given a record with the leader *

    @@ -85,15 +87,15 @@
      * the following event stream will be emitted:
      * 
      * start-record "1"
    - * start-entity "{@value Marc21EventNames#LEADER_ENTITY}"
    - * literal "{@value Marc21EventNames#RECORD_STATUS_LITERAL}": n
    - * literal "{@value Marc21EventNames#RECORD_TYPE_LITERAL}": o
    - * literal "{@value Marc21EventNames#BIBLIOGRAPHIC_LEVEL_LITERAL}": a
    - * literal "{@value Marc21EventNames#TYPE_OF_CONTROL_LITERAL}": " "
    - * literal "{@value Marc21EventNames#CHARACTER_CODING_LITERAL}": a
    - * literal "{@value Marc21EventNames#ENCODING_LEVEL_LITERAL}": z
    - * literal "{@value Marc21EventNames#CATALOGING_FORM_LITERAL}": u
    - * literal "{@value Marc21EventNames#MULTIPART_LEVEL_LITERAL}": " "
    + * start-entity {@value org.metafacture.biblio.marc21.Marc21EventNames#LEADER_ENTITY}
    + * literal {@value org.metafacture.biblio.marc21.Marc21EventNames#RECORD_STATUS_LITERAL}: n
    + * literal {@value org.metafacture.biblio.marc21.Marc21EventNames#RECORD_TYPE_LITERAL}: o
    + * literal {@value org.metafacture.biblio.marc21.Marc21EventNames#BIBLIOGRAPHIC_LEVEL_LITERAL}: a
    + * literal {@value org.metafacture.biblio.marc21.Marc21EventNames#TYPE_OF_CONTROL_LITERAL}: " "
    + * literal {@value org.metafacture.biblio.marc21.Marc21EventNames#CHARACTER_CODING_LITERAL}: a
    + * literal {@value org.metafacture.biblio.marc21.Marc21EventNames#ENCODING_LEVEL_LITERAL}: z
    + * literal {@value org.metafacture.biblio.marc21.Marc21EventNames#CATALOGING_FORM_LITERAL}: u
    + * literal {@value org.metafacture.biblio.marc21.Marc21EventNames#MULTIPART_LEVEL_LITERAL}: " "
      * end-entity
      * …
      * 
    @@ -123,7 +125,8 @@ * If the decoder receives an empty input string it is ignored and no stream * events are emitted. *

    - * If an error occurs during decoding, a {@link FormatException} is thrown. + * If an error occurs during decoding, a + * {@link org.metafacture.framework.FormatException} is thrown. * * @author Christoph Böhme * @see "ISO 2709:2008 Standard" @@ -132,35 +135,49 @@ */ @In(String.class) @Out(StreamReceiver.class) -@Description("Decodes MARC 21 records") +@Description("Decodes MARC 21 records (UTF-8 encoding expected).") @FluxCommand("decode-marc21") -public final class Marc21Decoder - extends DefaultObjectPipe { +public final class Marc21Decoder extends DefaultObjectPipe { + + public static final boolean EMIT_LEADER_AS_WHOLE = false; + public static final boolean IGNORE_MISSING_ID = false; private final FieldHandler fieldHandler = new Marc21Handler(); - private boolean ignoreMissingId; - private boolean emitLeaderAsWhole; + private boolean ignoreMissingId = IGNORE_MISSING_ID; + private boolean emitLeaderAsWhole = EMIT_LEADER_AS_WHOLE; /** - * Controls whether the decoder aborts processing if a record has no - * identifier. A {@link MissingIdException} is thrown in these cases. - * If this parameter is set to true then the identifier emitted with the - * start-record event of records without field "001" will - * be an empty string. + * Creates an instance of {@link Marc21Decoder}. + */ + public Marc21Decoder() { + } + + /** + * Controls whether the decoder aborts processing if a record has no identifier. + * A {@link MissingIdException} is thrown in these cases. If this parameter is + * set to true then the identifier emitted with the start-record event of + * records without field "001" will be an empty string. *

    - * The default value of {@code ignoreMissingId} is false. + * Default value: {@value #IGNORE_MISSING_ID} *

    * This parameter can be changed anytime during processing. The new value * becomes effective with the next record being processed. * - * @param ignoreMissingId - * true if missing identifiers should be silently ignored. + * @param ignoreMissingId true if missing identifiers should be silently + * ignored. */ public void setIgnoreMissingId(final boolean ignoreMissingId) { this.ignoreMissingId = ignoreMissingId; } + /** + * Gets the flag to decide whether to abort the processing of a record if it has + * no identifier. + * + * @return true if a missing identifier shouldn't abort processing, otherwise + * false + */ public boolean getIgnoreMissingId() { return ignoreMissingId; } @@ -169,16 +186,22 @@ public boolean getIgnoreMissingId() { * Controls whether the Record Leader should be emitted as a whole instead of * extracting the bibliographic information in the record leader. * - * @see MARC 21 - * Standard: Record Leader + * Default value: {@value #EMIT_LEADER_AS_WHOLE} * - * @param emitLeaderAsWhole - * true if the leader should be emitted as a whole. + * @see MARC 21 + * Standard: Record Leader + * @param emitLeaderAsWhole true if the leader should be emitted as a whole. */ public void setEmitLeaderAsWhole(final boolean emitLeaderAsWhole) { this.emitLeaderAsWhole = emitLeaderAsWhole; } + /** + * Gets the flag to decide whether the Record Leader is emitted as whole instead + * of extracting the bibliographic information in the record leader. + * + * @return true if the Record Leader is emitted as whole, otherwise false + */ public boolean getEmitLeaderAsWhole() { return emitLeaderAsWhole; } @@ -227,27 +250,28 @@ private String tryGetRecordId(final Record record) { private void emitLeader(final Record record) { getReceiver().startEntity(Marc21EventNames.LEADER_ENTITY); - if (emitLeaderAsWhole){ + if (emitLeaderAsWhole) { getReceiver().literal(Marc21EventNames.LEADER_ENTITY, record.getLabel()); - }else { - final char[] implCodes = record.getImplCodes(); - final char[] systemChars = record.getSystemChars(); - getReceiver().literal(Marc21EventNames.RECORD_STATUS_LITERAL, String.valueOf( - record.getRecordStatus())); - getReceiver().literal(Marc21EventNames.RECORD_TYPE_LITERAL, String.valueOf( - implCodes[Marc21Constants.RECORD_TYPE_INDEX])); - getReceiver().literal(Marc21EventNames.BIBLIOGRAPHIC_LEVEL_LITERAL, String.valueOf( - implCodes[Marc21Constants.BIBLIOGRAPHIC_LEVEL_INDEX])); - getReceiver().literal(Marc21EventNames.TYPE_OF_CONTROL_LITERAL, String.valueOf( - implCodes[Marc21Constants.TYPE_OF_CONTROL_INDEX])); - getReceiver().literal(Marc21EventNames.CHARACTER_CODING_LITERAL, String.valueOf( - implCodes[Marc21Constants.CHARACTER_CODING_INDEX])); - getReceiver().literal(Marc21EventNames.ENCODING_LEVEL_LITERAL, String.valueOf( - systemChars[Marc21Constants.ENCODING_LEVEL_INDEX])); - getReceiver().literal(Marc21EventNames.CATALOGING_FORM_LITERAL, String.valueOf( - systemChars[Marc21Constants.CATALOGING_FORM_INDEX])); - getReceiver().literal(Marc21EventNames.MULTIPART_LEVEL_LITERAL, String.valueOf( - systemChars[Marc21Constants.MULTIPART_LEVEL_INDEX])); + } + else { + final char[] implCodes = record.getImplCodes(); + final char[] systemChars = record.getSystemChars(); + getReceiver().literal(Marc21EventNames.RECORD_STATUS_LITERAL, String.valueOf( + record.getRecordStatus())); + getReceiver().literal(Marc21EventNames.RECORD_TYPE_LITERAL, String.valueOf( + implCodes[Marc21Constants.RECORD_TYPE_INDEX])); + getReceiver().literal(Marc21EventNames.BIBLIOGRAPHIC_LEVEL_LITERAL, String.valueOf( + implCodes[Marc21Constants.BIBLIOGRAPHIC_LEVEL_INDEX])); + getReceiver().literal(Marc21EventNames.TYPE_OF_CONTROL_LITERAL, String.valueOf( + implCodes[Marc21Constants.TYPE_OF_CONTROL_INDEX])); + getReceiver().literal(Marc21EventNames.CHARACTER_CODING_LITERAL, String.valueOf( + implCodes[Marc21Constants.CHARACTER_CODING_INDEX])); + getReceiver().literal(Marc21EventNames.ENCODING_LEVEL_LITERAL, String.valueOf( + systemChars[Marc21Constants.ENCODING_LEVEL_INDEX])); + getReceiver().literal(Marc21EventNames.CATALOGING_FORM_LITERAL, String.valueOf( + systemChars[Marc21Constants.CATALOGING_FORM_INDEX])); + getReceiver().literal(Marc21EventNames.MULTIPART_LEVEL_LITERAL, String.valueOf( + systemChars[Marc21Constants.MULTIPART_LEVEL_INDEX])); } getReceiver().endEntity(); } @@ -257,6 +281,9 @@ private void emitLeader(final Record record) { */ private final class Marc21Handler implements FieldHandler { + Marc21Handler() { + } + @Override public void referenceField(final char[] tag, final char[] implDefinedPart, final String value) { diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Encoder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Encoder.java index db0839de8..876f85203 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Encoder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21Encoder.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.marc21; -import java.util.Arrays; +package org.metafacture.biblio.marc21; import org.metafacture.biblio.iso2709.RecordBuilder; +import org.metafacture.biblio.iso2709.RecordFormat; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.FormatException; import org.metafacture.framework.ObjectReceiver; @@ -27,6 +27,8 @@ import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultStreamPipe; +import java.util.Arrays; + /** * Encodes a stream in MARC21 format. *

    @@ -70,7 +72,7 @@ public final class Marc21Encoder extends DefaultStreamPipe> { - private static final int NAME_LENGTH = Marc21Constants.MARC21_FORMAT.TAG_LENGTH + + private static final int NAME_LENGTH = RecordFormat.TAG_LENGTH + Marc21Constants.MARC21_FORMAT.getIndicatorLength(); private final RecordBuilder builder; @@ -79,10 +81,14 @@ public final class Marc21Encoder extends private boolean generateIdField; + /** + * Initializes the encoder with MARC 21 constants and charset. + */ public Marc21Encoder() { builder = new RecordBuilder(Marc21Constants.MARC21_FORMAT); builder.setCharset(Marc21Constants.MARC21_CHARSET); } + /** * Controls whether the record identifier field ("001") is * generated from the record id in the start-record event. If id field @@ -101,6 +107,11 @@ public void setGenerateIdField(final boolean generateIdField) { this.generateIdField = generateIdField; } + /** + * Gets the flag to decide whether the ID field is generated. + * + * @return true if the record ID is generated, otherwise false + */ public boolean getGenerateIdField() { return generateIdField; } @@ -117,8 +128,8 @@ public void startRecord(final String identifier) { private void initLeader() { builder.setRecordStatus(' '); - builder.setImplCodes(new char[]{ ' ', ' ', ' ', ' ' }); - builder.setSystemChars(new char[]{ ' ', ' ', ' ' }); + builder.setImplCodes(new char[]{' ', ' ', ' ', ' '}); + builder.setSystemChars(new char[]{' ', ' ', ' '}); builder.setReservedChar(Marc21Constants.RESERVED_CHAR); } @@ -136,7 +147,8 @@ public void startEntity(final String name) { } if (Marc21EventNames.LEADER_ENTITY.equals(name)) { state = State.IN_LEADER_ENTITY; - } else { + } + else { startField(name); state = State.IN_FIELD_ENTITY; } @@ -146,14 +158,13 @@ private void startField(final String name) { if (name.length() != NAME_LENGTH) { throw new FormatException("invalid entity name: " + name); } - final char[] tag = new char[Marc21Constants.MARC21_FORMAT.TAG_LENGTH]; + final char[] tag = new char[RecordFormat.TAG_LENGTH]; final char[] indicators = new char[Marc21Constants.MARC21_FORMAT.getIndicatorLength()]; name.getChars(0, tag.length, tag, 0); name.getChars(tag.length, name.length(), indicators, 0); builder.startDataField(tag, indicators); } - @Override public void endEntity() { if (state.equals(State.IN_FIELD_ENTITY)) { @@ -229,18 +240,17 @@ private void requireValidCode(final char code, final char[] validCodes) { return; } } - throw new FormatException("invalid code '" + code + "'; allowed codes are: " - + Arrays.toString(validCodes)); + throw new FormatException("invalid code '" + code + "'; allowed codes are: " + Arrays.toString(validCodes)); } private void processTopLevelLiteral(final String name, final String value) { - if (Marc21EventNames.MARCXML_TYPE_LITERAL.equals(name)) { - // MarcXmlHandler may output `type` literals. The - // information in these literals is not included in - // marc21 records. Therefore, we need to ignore - // these literals here. + if (Marc21EventNames.MARCXML_TYPE_LITERAL.equals(name)) { + // MarcXmlHandler may output `type` literals. The + // information in these literals is not included in + // marc21 records. Therefore, we need to ignore + // these literals here. return; - } + } builder.appendReferenceField(name.toCharArray(), value); } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21EventNames.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21EventNames.java index 1b0e6e1aa..38f148d53 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21EventNames.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/Marc21EventNames.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.marc21; /** @@ -22,127 +23,126 @@ */ public final class Marc21EventNames { - /** - * Name of the literal event which contains the value of the - * type attribute of the record element. - */ - public static final String MARCXML_TYPE_LITERAL = "type"; + /** + * Name of the literal event which contains the value of the + * type attribute of the record element. + */ + public static final String MARCXML_TYPE_LITERAL = "type"; - /** - * Name of the entity event which contains the bibliographic - * information in the record leader. - * - * @see MARC 21 - * Standard: Record Leader - */ - public static final String LEADER_ENTITY = "leader"; + /** + * Name of the entity event which contains the bibliographic + * information in the record leader. + * + * @see MARC 21 + * Standard: Record Leader + */ + public static final String LEADER_ENTITY = "leader"; - /** - * Name of the literal event emitted for the record status field. - *

    - * The name of the literal is "{@value #RECORD_STATUS_LITERAL}". - *

    - * The record status is specified at position 5 in the record leader. - * - * @see MARC 21 - * Standard: Record Leader - */ - public static final String RECORD_STATUS_LITERAL = "status"; + /** + * Name of the literal event emitted for the record status field. + *

    + * The name of the literal is {@value #RECORD_STATUS_LITERAL}. + *

    + * The record status is specified at position 5 in the record leader. + * + * @see MARC 21 + * Standard: Record Leader + */ + public static final String RECORD_STATUS_LITERAL = "status"; - /** - * Name of the literal event emitted for the bibliographic level - * field. - *

    - * The name of the literal is - * "{@value #BIBLIOGRAPHIC_LEVEL_LITERAL}". - *

    - * The bibliographic level is specified at position 7 in the record leader. - * - * @see MARC 21 - * Standard: Record Leader - */ - public static final String BIBLIOGRAPHIC_LEVEL_LITERAL = "bibliographicLevel"; + /** + * Name of the literal event emitted for the bibliographic level + * field. + *

    + * The name of the literal is {@value #BIBLIOGRAPHIC_LEVEL_LITERAL}. + *

    + * The bibliographic level is specified at position 7 in the record leader. + * + * @see MARC 21 + * Standard: Record Leader + */ + public static final String BIBLIOGRAPHIC_LEVEL_LITERAL = "bibliographicLevel"; - /** - * Name of the literal event emitted for the type of control field. - *

    - * The name of the literal is "{@value #TYPE_OF_CONTROL_LITERAL}". - *

    - * The type of control is specified at position 8 in the record leader. - * - * @see MARC 21 - * Standard: Record Leader - */ - public static final String TYPE_OF_CONTROL_LITERAL = "typeOfControl"; + /** + * Name of the literal event emitted for the type of control field. + *

    + * The name of the literal is {@value #TYPE_OF_CONTROL_LITERAL}. + *

    + * The type of control is specified at position 8 in the record leader. + * + * @see MARC 21 + * Standard: Record Leader + */ + public static final String TYPE_OF_CONTROL_LITERAL = "typeOfControl"; - /** - * Name of the literal event emitted for the character coding scheme - * field. - *

    - * The name of the literal is "{@value #CHARACTER_CODING_LITERAL}". - *

    - * The character coding scheme is specified at position 9 in the record - * leader. - * - * @see MARC 21 - * Standard: Record Leader - */ - public static final String CHARACTER_CODING_LITERAL = "characterCodingScheme"; + /** + * Name of the literal event emitted for the character coding scheme + * field. + *

    + * The name of the literal is {@value #CHARACTER_CODING_LITERAL}. + *

    + * The character coding scheme is specified at position 9 in the record + * leader. + * + * @see MARC 21 + * Standard: Record Leader + */ + public static final String CHARACTER_CODING_LITERAL = "characterCodingScheme"; - /** - * Name of the literal event emitted for the encoding level field. - *

    - * The name of the literal is "{@value #ENCODING_LEVEL_LITERAL}". - *

    - * The encoding level is specified at position 17 in the record leader. - * - * @see MARC 21 - * Standard: Record Leader - */ - public static final String ENCODING_LEVEL_LITERAL = "encodingLevel"; + /** + * Name of the literal event emitted for the encoding level field. + *

    + * The name of the literal is {@value #ENCODING_LEVEL_LITERAL}. + *

    + * The encoding level is specified at position 17 in the record leader. + * + * @see MARC 21 + * Standard: Record Leader + */ + public static final String ENCODING_LEVEL_LITERAL = "encodingLevel"; - /** - * Name of the literal event emitted for the descriptive cataloging - * form field. - *

    - * The name of the literal is "{@value #CATALOGING_FORM_LITERAL}". - *

    - * The descriptive cataloging form is specified at position 18 in the record - * leader. - * - * @see MARC 21 - * Standard: Record Leader - */ - public static final String CATALOGING_FORM_LITERAL = "catalogingForm"; + /** + * Name of the literal event emitted for the descriptive cataloging + * form field. + *

    + * The name of the literal is {@value #CATALOGING_FORM_LITERAL}. + *

    + * The descriptive cataloging form is specified at position 18 in the record + * leader. + * + * @see MARC 21 + * Standard: Record Leader + */ + public static final String CATALOGING_FORM_LITERAL = "catalogingForm"; - /** - * Name of the literal event emitted for the multipart resource - * record level field. - *

    - * The name of the literal is "{@value #MULTIPART_LEVEL_LITERAL}". - *

    - * The multipart resource record level is specified at position 19 in the - * record leader. - * - * @see MARC 21 - * Standard: Record Leader - */ - public static final String MULTIPART_LEVEL_LITERAL = "multipartLevel"; + /** + * Name of the literal event emitted for the multipart resource + * record level field. + *

    + * The name of the literal is {@value #MULTIPART_LEVEL_LITERAL}. + *

    + * The multipart resource record level is specified at position 19 in the + * record leader. + * + * @see MARC 21 + * Standard: Record Leader + */ + public static final String MULTIPART_LEVEL_LITERAL = "multipartLevel"; - /** - * Name of the literal event emitted for the type of record field. - *

    - * The name of the literal is "{@value #RECORD_TYPE_LITERAL}". - *

    - * The type of record is specified at position 6 in the record leader. - * - * @see MARC 21 - * Standard: Record Leader - */ - public static final String RECORD_TYPE_LITERAL = "type"; + /** + * Name of the literal event emitted for the type of record field. + *

    + * The name of the literal is {@value #RECORD_TYPE_LITERAL}. + *

    + * The type of record is specified at position 6 in the record leader. + * + * @see MARC 21 + * Standard: Record Leader + */ + public static final String RECORD_TYPE_LITERAL = "type"; - private Marc21EventNames() { - throw new AssertionError("class should not be instantiated"); - } + private Marc21EventNames() { + throw new AssertionError("class should not be instantiated"); + } } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlEncoder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlEncoder.java index bb81741d5..0b8202ec0 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlEncoder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlEncoder.java @@ -15,8 +15,6 @@ package org.metafacture.biblio.marc21; -import java.util.Collections; - import org.metafacture.commons.XmlUtil; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.MetafactureException; @@ -27,6 +25,10 @@ import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultStreamPipe; +import java.util.Arrays; +import java.util.Collections; +import java.util.function.Function; + /** * Encodes a stream into MARCXML. * @@ -39,74 +41,145 @@ @Out(String.class) @FluxCommand("encode-marcxml") public final class MarcXmlEncoder extends DefaultStreamPipe> { + + public static final String NAMESPACE_NAME = "marc"; + public static final String XML_ENCODING = "UTF-8"; + public static final String XML_VERSION = "1.0"; + public static final boolean PRETTY_PRINTED = true; + public static final boolean OMIT_XML_DECLARATION = false; + private static final String ROOT_OPEN = ""; private static final String ROOT_CLOSE = ""; - private static final String RECORD_OPEN = ""; - private static final String RECORD_CLOSE = ""; + private enum Tag { + + collection(" xmlns%s=\"" + NAMESPACE + "\"%s"), + controlfield(" tag=\"%s\""), + datafield(" tag=\"%s\" ind1=\"%s\" ind2=\"%s\""), + leader(""), + record(""), + subfield(" code=\"%s\""); + + private static final String OPEN_TEMPLATE = "<%%s%s%s>"; + private static final String CLOSE_TEMPLATE = ""; + + private final String openTemplate; + private final String closeTemplate; + + Tag(final String template) { + openTemplate = String.format(OPEN_TEMPLATE, name(), template); + closeTemplate = String.format(CLOSE_TEMPLATE, name()); + } + + public String open(final Object[] args) { + return String.format(openTemplate, args); + } + + public String close(final Object[] args) { + return String.format(closeTemplate, args); + } - private static final String CONTROLFIELD_OPEN_TEMPLATE = ""; - private static final String CONTROLFIELD_CLOSE = ""; + } - private static final String DATAFIELD_OPEN_TEMPLATE = ""; - private static final String DATAFIELD_CLOSE = ""; + private static final String NAMESPACE = "http://www.loc.gov/MARC21/slim"; + private static final String NAMESPACE_PREFIX = NAMESPACE_NAME + ":"; + private static final String NAMESPACE_SUFFIX = ":" + NAMESPACE_NAME; - private static final String SUBFIELD_OPEN_TEMPLATE = ""; - private static final String SUBFIELD_CLOSE = ""; + private static final String SCHEMA_ATTRIBUTES = " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"" + NAMESPACE + " http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd\""; - private static final String LEADER_OPEN_TEMPLATE = ""; - private static final String LEADER_CLOSE_TEMPLATE = ""; + private static final String ATTRIBUTE_TEMPLATE = " %s=\"%s\""; private static final String NEW_LINE = "\n"; private static final String INDENT = "\t"; + private static final String EMPTY = ""; private static final String XML_DECLARATION_TEMPLATE = ""; - private final StringBuilder builder; + private static final int LEADER_ENTITY_LENGTH = 5; - private boolean atStreamStart; + private static final int IND1_BEGIN = 3; + private static final int IND1_END = 4; + private static final int IND2_BEGIN = 4; + private static final int IND2_END = 5; + private static final int TAG_BEGIN = 0; + private static final int TAG_END = 3; - private boolean omitXmlDeclaration; - private String xmlVersion; - private String xmlEncoding; + private final StringBuilder builder = new StringBuilder(); - private String currentEntity; - private int indentationLevel; - private boolean formatted; + private boolean atStreamStart = true; - public MarcXmlEncoder() { - this.builder = new StringBuilder(); - this.atStreamStart = true; + private boolean omitXmlDeclaration = OMIT_XML_DECLARATION; + private String xmlVersion = XML_VERSION; + private String xmlEncoding = XML_ENCODING; - this.omitXmlDeclaration = false; - this.xmlVersion = "1.0"; - this.xmlEncoding = "UTF-8"; + private String currentEntity = ""; + + private boolean emitNamespace = true; + private Object[] namespacePrefix = new Object[]{emitNamespace ? NAMESPACE_PREFIX : EMPTY}; + + private int indentationLevel; + private boolean formatted = PRETTY_PRINTED; + private int recordAttributeOffset; - this.currentEntity = ""; + /** + * Creates an instance of {@link MarcXmlEncoder}. + */ + public MarcXmlEncoder() { + } - this.indentationLevel = 0; - this.formatted = true; + /** + * Sets the flag to decide whether to emit the {@value #NAMESPACE_NAME} + * namespace + * + * @param emitNamespace true if the namespace is emitted, otherwise false + */ + public void setEmitNamespace(final boolean emitNamespace) { + this.emitNamespace = emitNamespace; + namespacePrefix = new Object[]{emitNamespace ? NAMESPACE_PREFIX : EMPTY}; } - public void omitXmlDeclaration(boolean omitXmlDeclaration) { - this.omitXmlDeclaration = omitXmlDeclaration; + /** + * Sets the flag to decide whether to omit the XML declaration. + * + * Default value: {@value #OMIT_XML_DECLARATION} + * + * @param currentOmitXmlDeclaration true if the XML declaration is omitted, otherwise + * false + */ + public void omitXmlDeclaration(final boolean currentOmitXmlDeclaration) { + omitXmlDeclaration = currentOmitXmlDeclaration; } - public void setXmlVersion(String xmlVersion) { + /** + * Sets the XML version. + * + * Default value: {@value #XML_VERSION} + * + * @param xmlVersion the XML version + */ + public void setXmlVersion(final String xmlVersion) { this.xmlVersion = xmlVersion; } - public void setXmlEncoding(String xmlEncoding) { + /** + * Sets the XML encoding. + * + * Default value: {@value #XML_ENCODING} + * + * @param xmlEncoding the XML encoding + */ + public void setXmlEncoding(final String xmlEncoding) { this.xmlEncoding = xmlEncoding; } /** - * Formats the resulting xml, by indentation. - * - * @param formatted - * True, if formatting is activated. + * Formats the resulting xml by indentation. Aka "pretty printing". + * + * Default value: {@value #PRETTY_PRINTED} + * + * @param formatted true if formatting is activated, otherwise false */ - public void setFormatted(boolean formatted) { + public void setFormatted(final boolean formatted) { this.formatted = formatted; } @@ -117,14 +190,15 @@ public void startRecord(final String identifier) { writeHeader(); prettyPrintNewLine(); } - writeRaw(ROOT_OPEN); + writeTag(Tag.collection::open, emitNamespace ? NAMESPACE_SUFFIX : EMPTY, emitNamespace ? SCHEMA_ATTRIBUTES : EMPTY); prettyPrintNewLine(); incrementIndentationLevel(); } atStreamStart = false; prettyPrintIndentation(); - writeRaw(RECORD_OPEN); + writeTag(Tag.record::open); + recordAttributeOffset = builder.length() - 1; prettyPrintNewLine(); incrementIndentationLevel(); @@ -134,7 +208,7 @@ public void startRecord(final String identifier) { public void endRecord() { decrementIndentationLevel(); prettyPrintIndentation(); - writeRaw(RECORD_CLOSE); + writeTag(Tag.record::close); prettyPrintNewLine(); sendAndClearData(); } @@ -143,17 +217,17 @@ public void endRecord() { public void startEntity(final String name) { currentEntity = name; if (!name.equals(Marc21EventNames.LEADER_ENTITY)) { - if (name.length() != 5) { - String message = String.format("Entity too short." + "Got a string ('%s') of length %d." - + "Expected a length of 5 (field + indicators).", name, name.length()); + if (name.length() != LEADER_ENTITY_LENGTH) { + final String message = String.format("Entity too short." + "Got a string ('%s') of length %d." + + "Expected a length of " + LEADER_ENTITY_LENGTH + " (field + indicators).", name, name.length()); throw new MetafactureException(message); } - String tag = name.substring(0, 3); - String ind1 = name.substring(3, 4); - String ind2 = name.substring(4, 5); + final String tag = name.substring(TAG_BEGIN, TAG_END); + final String ind1 = name.substring(IND1_BEGIN, IND1_END); + final String ind2 = name.substring(IND2_BEGIN, IND2_END); prettyPrintIndentation(); - writeRaw(String.format(DATAFIELD_OPEN_TEMPLATE, tag, ind1, ind2)); + writeTag(Tag.datafield::open, tag, ind1, ind2); prettyPrintNewLine(); incrementIndentationLevel(); } @@ -164,7 +238,7 @@ public void endEntity() { if (!currentEntity.equals(Marc21EventNames.LEADER_ENTITY)) { decrementIndentationLevel(); prettyPrintIndentation(); - writeRaw(DATAFIELD_CLOSE); + writeTag(Tag.datafield::close); prettyPrintNewLine(); } currentEntity = ""; @@ -172,27 +246,29 @@ public void endEntity() { @Override public void literal(final String name, final String value) { - if (currentEntity.equals("")) { - prettyPrintIndentation(); - writeRaw(String.format(CONTROLFIELD_OPEN_TEMPLATE, name)); - if (value != null) - writeEscaped(value.trim()); - writeRaw(CONTROLFIELD_CLOSE); - prettyPrintNewLine(); - } else if (!currentEntity.equals(Marc21EventNames.LEADER_ENTITY)) { - prettyPrintIndentation(); - writeRaw(String.format(SUBFIELD_OPEN_TEMPLATE, name)); - writeEscaped(value.trim()); - writeRaw(SUBFIELD_CLOSE); - prettyPrintNewLine(); - } else { - if (name.equals(Marc21EventNames.LEADER_ENTITY)) { + if ("".equals(currentEntity)) { + if (name.equals(Marc21EventNames.MARCXML_TYPE_LITERAL)) { + if (value != null) { + builder.insert(recordAttributeOffset, String.format(ATTRIBUTE_TEMPLATE, name, value)); + } + } + else if (!writeLeader(name, value)) { prettyPrintIndentation(); - writeRaw(LEADER_OPEN_TEMPLATE + value + LEADER_CLOSE_TEMPLATE); + writeTag(Tag.controlfield::open, name); + if (value != null) { + writeEscaped(value.trim()); + } + writeTag(Tag.controlfield::close); prettyPrintNewLine(); } } - + else if (!writeLeader(currentEntity, value)) { + prettyPrintIndentation(); + writeTag(Tag.subfield::open, name); + writeEscaped(value.trim()); + writeTag(Tag.subfield::close); + prettyPrintNewLine(); + } } @Override @@ -227,22 +303,51 @@ private void writeHeader() { /** Closes the root tag */ private void writeFooter() { - writeRaw(ROOT_CLOSE); + writeTag(Tag.collection::close); } - /** Writes a unescaped sequence */ + /** + * Writes an unescaped sequence. + * + * @param str the unescaped sequence to be written + */ private void writeRaw(final String str) { builder.append(str); } - /** Writes a escaped sequence */ + /** + * Writes an escaped sequence. + * + * @param str the unescaped sequence to be written + */ private void writeEscaped(final String str) { builder.append(XmlUtil.escape(str, false)); } + private boolean writeLeader(final String name, final String value) { + if (name.equals(Marc21EventNames.LEADER_ENTITY)) { + prettyPrintIndentation(); + writeTag(Tag.leader::open); + writeRaw(value); + writeTag(Tag.leader::close); + prettyPrintNewLine(); + + return true; + } + else { + return false; + } + } + + private void writeTag(final Function function, final Object... args) { + final Object[] allArgs = Arrays.copyOf(namespacePrefix, namespacePrefix.length + args.length); + System.arraycopy(args, 0, allArgs, namespacePrefix.length, args.length); + writeRaw(function.apply(allArgs)); + } + private void prettyPrintIndentation() { if (formatted) { - String prefix = String.join("", Collections.nCopies(indentationLevel, INDENT)); + final String prefix = String.join("", Collections.nCopies(indentationLevel, INDENT)); builder.append(prefix); } } @@ -256,5 +361,7 @@ private void prettyPrintNewLine() { private void sendAndClearData() { getReceiver().process(builder.toString()); builder.delete(0, builder.length()); + recordAttributeOffset = 0; } -} \ No newline at end of file + +} diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlHandler.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlHandler.java index 6f1264140..76340b66b 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlHandler.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlHandler.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.marc21; import org.metafacture.framework.FluxCommand; @@ -22,10 +23,10 @@ import org.metafacture.framework.annotations.In; import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultXmlPipe; + import org.xml.sax.Attributes; import org.xml.sax.SAXException; - /** * A marc xml reader. * @author Markus Michael Geipel @@ -37,17 +38,33 @@ @FluxCommand("handle-marcxml") public final class MarcXmlHandler extends DefaultXmlPipe { + public static final String NAMESPACE = "http://www.loc.gov/MARC21/slim"; + private static final String SUBFIELD = "subfield"; private static final String DATAFIELD = "datafield"; private static final String CONTROLFIELD = "controlfield"; private static final String RECORD = "record"; - private static final String NAMESPACE = "http://www.loc.gov/MARC21/slim"; private static final String LEADER = "leader"; private static final String TYPE = "type"; + + private String attributeMarker = DEFAULT_ATTRIBUTE_MARKER; private String currentTag = ""; private String namespace = NAMESPACE; private StringBuilder builder = new StringBuilder(); + /** + * Creates an instance of {@link MarcXmlHandler}. + */ + public MarcXmlHandler() { + } + + /** + * Sets the namespace. + * + * Default value: {@value #NAMESPACE} + * + * @param namespace the namespace + */ public void setNamespace(final String namespace) { this.namespace = namespace; } @@ -56,40 +73,65 @@ private boolean checkNamespace(final String uri) { return namespace == null || namespace.equals(uri); } + /** + * Sets the attribute marker. + * + * Default value: + * {@value org.metafacture.framework.helpers.DefaultXmlPipe#DEFAULT_ATTRIBUTE_MARKER} + * + * @param attributeMarker the attribute marker + */ + public void setAttributeMarker(final String attributeMarker) { + this.attributeMarker = attributeMarker; + } + + /** + * Gets the attribute marker. + * + * @return the attribute marker + */ + public String getAttributeMarker() { + return attributeMarker; + } + @Override - public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) - throws SAXException { - if(SUBFIELD.equals(localName)){ - builder = new StringBuilder(); - currentTag = attributes.getValue("code"); - }else if(DATAFIELD.equals(localName)){ - getReceiver().startEntity(attributes.getValue("tag") + attributes.getValue("ind1") + attributes.getValue("ind2")); - }else if(CONTROLFIELD.equals(localName)){ - builder = new StringBuilder(); - currentTag = attributes.getValue("tag"); - }else if(RECORD.equals(localName) && checkNamespace(uri)){ - getReceiver().startRecord(""); - getReceiver().literal(TYPE, attributes.getValue(TYPE)); - }else if(LEADER.equals(localName)){ - builder = new StringBuilder(); - currentTag = LEADER; - } + public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { + if (SUBFIELD.equals(localName)) { + builder = new StringBuilder(); + currentTag = attributes.getValue("code"); + } + else if (DATAFIELD.equals(localName)) { + getReceiver().startEntity(attributes.getValue("tag") + attributes.getValue("ind1") + attributes.getValue("ind2")); + } + else if (CONTROLFIELD.equals(localName)) { + builder = new StringBuilder(); + currentTag = attributes.getValue("tag"); + } + else if (RECORD.equals(localName) && checkNamespace(uri)) { + getReceiver().startRecord(""); + getReceiver().literal(attributeMarker + TYPE, attributes.getValue(TYPE)); + } + else if (LEADER.equals(localName)) { + builder = new StringBuilder(); + currentTag = LEADER; + } } @Override public void endElement(final String uri, final String localName, final String qName) throws SAXException { - if(SUBFIELD.equals(localName)){ + if (SUBFIELD.equals(localName)) { getReceiver().literal(currentTag, builder.toString().trim()); - - }else if(DATAFIELD.equals(localName)){ + } + else if (DATAFIELD.equals(localName)) { getReceiver().endEntity(); - }else if(CONTROLFIELD.equals(localName)){ - getReceiver().literal(currentTag, builder.toString().trim()); - - }else if(RECORD.equals(localName) && checkNamespace(uri)){ + } + else if (CONTROLFIELD.equals(localName)) { + getReceiver().literal(currentTag, builder.toString()); + } + else if (RECORD.equals(localName) && checkNamespace(uri)) { getReceiver().endRecord(); - - }else if(LEADER.equals(localName)){ + } + else if (LEADER.equals(localName)) { getReceiver().literal(currentTag, builder.toString()); } } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaConstants.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaConstants.java index c549fb4a3..e36a4be3a 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaConstants.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaConstants.java @@ -26,31 +26,31 @@ * */ enum PicaConstants { - // We use '\0' for null/empty - RECORD_MARKER('\u001d', '\n'), // - FIELD_MARKER('\u001e', '\0'), // - SUBFIELD_MARKER('\u001f', '$'), // - FIELD_END_MARKER('\n', '\n'), // - NO_MARKER('\0', '\0'); + // We use '\0' for null/empty + RECORD_MARKER('\u001d', '\n'), // + FIELD_MARKER('\u001e', '\0'), // + SUBFIELD_MARKER('\u001f', '$'), // + FIELD_END_MARKER('\n', '\n'), // + NO_MARKER('\0', '\0'); - char normalized; - char nonNormalized; + private final char normalized; + private final char nonNormalized; - PicaConstants(char normalized, char nonNormalized) { - this.normalized = normalized; - this.nonNormalized = nonNormalized; - } + PicaConstants(final char normalized, final char nonNormalized) { + this.normalized = normalized; + this.nonNormalized = nonNormalized; + } - public char get(boolean isNormalized) { - return isNormalized ? normalized : nonNormalized; - } + public char get(final boolean isNormalized) { + return isNormalized ? normalized : nonNormalized; + } - public static PicaConstants from(boolean isNormalized, char ch) { - for (PicaConstants value : values()) { - if (ch == (isNormalized ? value.normalized : value.nonNormalized)) { - return value; - } - } - return NO_MARKER; - } -} \ No newline at end of file + public static PicaConstants from(final boolean isNormalized, final char ch) { + for (final PicaConstants value : values()) { + if (ch == (isNormalized ? value.normalized : value.nonNormalized)) { + return value; + } + } + return NO_MARKER; + } +} diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaDecoder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaDecoder.java index f0c818648..1b3c7cae2 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaDecoder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaDecoder.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.pica; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +package org.metafacture.biblio.pica; import org.metafacture.commons.StringUtil; import org.metafacture.framework.FluxCommand; @@ -27,6 +25,9 @@ import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultObjectPipe; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * Parses pica+ records. The parser only parses single records. A string * containing multiple records must be split into individual records before @@ -107,14 +108,14 @@ * is removed). This can be changed by setting * {@link #setTrimFieldNames(boolean)} to false. *

    - * The record id emitted with the start-record event is extracted from + * The record ID emitted with the start-record event is extracted from * one of the following non-normalized pica+ fields: *

      *
    • 003@ $0 *
    • 107F $0 *
    • 203@ $0 (this field may have an optional occurrence marker) *
    - * The value of the first matching field is used as the record id. The $0 + * The value of the first matching field is used as the record ID. The $0 * subfield must be the first subfield in the field. If * {@link #setIgnoreMissingIdn(boolean)} is false and no matching field is not * found in the record a {@link MissingIdException} is thrown otherwise the @@ -153,11 +154,8 @@ @In(String.class) @Out(StreamReceiver.class) @FluxCommand("decode-pica") -public final class PicaDecoder - extends DefaultObjectPipe { +public final class PicaDecoder extends DefaultObjectPipe { - private static String START_MARKERS; - private static Pattern ID_FIELDS_PATTERN; private static final int BUFFER_SIZE = 1024 * 1024; private Matcher idFieldMatcher; @@ -170,11 +168,21 @@ public final class PicaDecoder private boolean ignoreMissingIdn; private boolean isNormalized; + /** + * Creates an instance of {@link PicaDecoder}. Sets the input to read as + * normalized pica+. + */ public PicaDecoder() { this(true); } - public PicaDecoder(boolean normalized) { + /** + * Creates an instance of {@link PicaDecoder}. Sets the input to read as + * normalized or non-normalized pica+. + * + * @param normalized true if input is read as normalized pica+, otherwiese false + */ + public PicaDecoder(final boolean normalized) { setNormalizedSerialization(normalized); } @@ -185,23 +193,21 @@ public PicaDecoder(boolean normalized) { * @param normalized if true, the input is treated as normalized pica+ ; * if false, it's treated as non-normalized. */ - public void setNormalizedSerialization(boolean normalized) { + public void setNormalizedSerialization(final boolean normalized) { this.isNormalized = normalized; - makeConstants(); - } - private void makeConstants() { - START_MARKERS = "(?:^|" + PicaConstants.FIELD_MARKER.get(isNormalized) + "|" - + PicaConstants.FIELD_END_MARKER.get(isNormalized) + "|" - + PicaConstants.RECORD_MARKER.get(isNormalized) + "|.*\n" + ")"; - ID_FIELDS_PATTERN = Pattern - .compile(START_MARKERS + "(?:003@|203@(?:/..+)?|107F) " - + " ?(\\" + PicaConstants.SUBFIELD_MARKER.get(isNormalized) + "|" - + PicaConstants.SUBFIELD_MARKER.get(isNormalized) + ")0"); - idFieldMatcher = ID_FIELDS_PATTERN.matcher(""); + final String startMarkers = "(?:^|" + PicaConstants.FIELD_MARKER.get(isNormalized) + "|" + + PicaConstants.FIELD_END_MARKER.get(isNormalized) + "|" + + PicaConstants.RECORD_MARKER.get(isNormalized) + "|.*\n" + ")"; + final Pattern idFieldsPattern = Pattern + .compile(startMarkers + "(?:003@|203@(?:/..+)?|107F) " + + " ?(\\" + PicaConstants.SUBFIELD_MARKER.get(isNormalized) + "|" + + PicaConstants.SUBFIELD_MARKER.get(isNormalized) + ")0"); + idFieldMatcher = idFieldsPattern.matcher(""); } + /** - * Controls whether records having no record id are reported as faulty. By + * Controls whether records having no record ID are reported as faulty. By * default such records are reported by the {@code PicaDecoder} by throwing * a {@link MissingIdException}. *

    @@ -210,7 +216,7 @@ private void makeConstants() { *

    * Default value: {@code false} * - * @param ignoreMissingIdn if true, missing record ids do not trigger a + * @param ignoreMissingIdn if true, missing record IDs do not trigger a * {@link MissingIdException} but an empty string is * used as record identifier instead. */ @@ -218,12 +224,17 @@ public void setIgnoreMissingIdn(final boolean ignoreMissingIdn) { this.ignoreMissingIdn = ignoreMissingIdn; } + /** + * Gets the flag to decide whether records without a record ID are processed. + * + * @return true if the ID of a record can be absent, otherwise false + */ public boolean getIgnoreMissingIdn() { return ignoreMissingIdn; } /** - * Controls whether decomposed unicode characters in field values are + * Controls whether decomposed Unicode characters in field values are * normalised to their precomposed version. By default no normalisation is * applied. The normalisation is only applied to values not to field or * subfield names. @@ -233,13 +244,18 @@ public boolean getIgnoreMissingIdn() { *

    * Default value: {@code false} * - * @param normalizeUTF8 if true, decomposed unicode characters in values are + * @param normalizeUTF8 if true, decomposed Unicode characters in values are * normalised to their precomposed version. */ public void setNormalizeUTF8(final boolean normalizeUTF8) { parserContext.setNormalizeUTF8(normalizeUTF8); } + /** + * Gets the flag to decide whether the record is UTF-8 normalized. + * + * @return true if the record is UTF-8 normalized, otherwise false + */ public boolean getNormalizeUTF8() { return parserContext.getNormalizeUTF8(); } @@ -259,6 +275,11 @@ public void setSkipEmptyFields(final boolean skipEmptyFields) { parserContext.setSkipEmptyFields(skipEmptyFields); } + /** + * Gets the flag to decide whether to skip empty fields. + * + * @return true if empty fields are ignored, otherwise false + */ public boolean getSkipEmptyFields() { return parserContext.getSkipEmptyFields(); } @@ -278,9 +299,15 @@ public void setTrimFieldNames(final boolean trimFieldNames) { parserContext.setTrimFieldNames(trimFieldNames); } + /** + * Gets the flag to decide whether the field names are trimmed. + * + * @return true if the field names are trimmed, otherwise false + */ public boolean getTrimFieldNames() { return parserContext.getTrimFieldNames(); } + @Override public void process(final String record) { assert !isClosed(); diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaEncoder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaEncoder.java index 93934c022..540dfbe02 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaEncoder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaEncoder.java @@ -13,12 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.pica; -import java.text.Normalizer; -import java.text.Normalizer.Form; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +package org.metafacture.biblio.pica; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.FormatException; @@ -30,6 +26,10 @@ import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultStreamPipe; +import java.text.Normalizer; +import java.text.Normalizer.Form; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Encodes an event stream in pica+ format. @@ -54,6 +54,8 @@ @FluxCommand("encode-pica") public final class PicaEncoder extends DefaultStreamPipe> { + public static final boolean IGNORE_RECORD_ID = false; + private static final String FIELD_DELIMITER = "\u001e"; private static final String SUB_DELIMITER = "\u001f"; private static final String FIELD_IDN_INTERN = "003@"; @@ -62,12 +64,18 @@ public final class PicaEncoder extends DefaultStreamPipe> private static StringBuilder builder = new StringBuilder(); //Result of the encoding process - private boolean entityOpen; //Flag to inform whether an entity is opened. - private boolean idnControlSubField; //Flag to inform whether it is the 003@ field. - private boolean ignoreRecordId; //Flag to decide whether the record Id is checked. + private boolean entityOpen; // Flag to inform whether an entity is opened. + private boolean idnControlSubField; // Flag to inform whether it is the 003@ field. + private boolean ignoreRecordId = IGNORE_RECORD_ID; // Flag to decide whether the record ID is checked. private String id; + /** + * Creates an instance of {@link PicaEncoder}. + */ + public PicaEncoder() { + } + @Override public void startRecord(final String recordId) { // the name is a idn, which should be found in the encoded data under 003@. @@ -78,10 +86,23 @@ public void startRecord(final String recordId) { this.entityOpen = false; } + /** + * Sets the flag to decide whether the record ID is checked. + * + * Default value: {@value #IGNORE_RECORD_ID} + * + * @param ignoreRecordId true if the record ID should be ignored, otherwise + * false + */ public void setIgnoreRecordId(final boolean ignoreRecordId) { this.ignoreRecordId = ignoreRecordId; } + /** + * Gets the flag to decide whether the record ID is checked. + * + * @return true if the record ID is ignored, otherwise false + */ public boolean getIgnoreRecordId() { return this.ignoreRecordId; } @@ -97,7 +118,7 @@ public void startEntity(final String name) { if (entityOpen) { //No nested entities are allowed in pica+. throw new FormatException(name); } - builder.append(name.trim()+ " "); + builder.append(name.trim() + " "); idnControlSubField = !ignoreRecordId && FIELD_IDN_INTERN.equals(name.trim()); //Now literals can be opened but no more entities. @@ -115,16 +136,16 @@ public void literal(final String name, final String value) { } final String valueNew = Normalizer.normalize(value, Form.NFD); if (idnControlSubField) { - // it is a 003@ field, the same record id delivered with record should follow + // it is a 003@ field, the same record ID delivered with record should follow if (!this.id.equals(value)) { throw new MissingIdException(value); } - idnControlSubField = false; //only one record Id will be checked. + idnControlSubField = false; //only one record ID will be checked. } builder.append(SUB_DELIMITER); builder.append(name); builder.append(valueNew); -} + } @Override public void endEntity() { diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaMultiscriptRemodeler.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaMultiscriptRemodeler.java index d5728ea96..3551105f0 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaMultiscriptRemodeler.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaMultiscriptRemodeler.java @@ -13,11 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.pica; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; +package org.metafacture.biblio.pica; import org.metafacture.flowcontrol.StreamBuffer; import org.metafacture.framework.FluxCommand; @@ -27,6 +24,10 @@ import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultStreamPipe; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + /** * Groups multiscript fields in entities. *

    @@ -103,8 +104,7 @@ @Out(StreamReceiver.class) @Description("Groups multiscript fields in entities") @FluxCommand("remodel-pica-multiscript") -public final class PicaMultiscriptRemodeler extends - DefaultStreamPipe { +public final class PicaMultiscriptRemodeler extends DefaultStreamPipe { public static final String ENTITY_NAME_FOR_LATIN = "Latin"; public static final String ENTITY_NAME_FOR_NON_LATIN_LR = "NonLatinLR"; @@ -124,6 +124,12 @@ public final class PicaMultiscriptRemodeler extends private final SortedMap bufferedFields = new TreeMap(); + /** + * Creates an instance of {@link PicaMultiscriptRemodeler}. + */ + public PicaMultiscriptRemodeler() { + } + @Override public void startRecord(final String identifier) { getReceiver().startRecord(identifier); @@ -143,22 +149,24 @@ public void endRecord() { @Override public void startEntity(final String name) { currentField = new BufferedField(name); - currentField.stream.setReceiver(getReceiver()); + currentField.getStream().setReceiver(getReceiver()); - if (!lastField.name.equals(currentField.name)) { + if (!lastField.getName().equals(currentField.getName())) { emitAsSingleMultiscriptFields(bufferedFields); } } @Override public void endEntity() { - if (currentField.group == null || currentField.script == null) { + if (currentField.getGroup() == null || currentField.getScript() == null) { emitNonMultiscriptField(); - } else { - if (bufferedFields.containsKey(currentField.group)) { + } + else { + if (bufferedFields.containsKey(currentField.getGroup())) { emitAsSingleMultiscriptFields(getSingleMultiscriptFieldsBeforeCurrentField()); - emitRemodeledMultiscriptField(bufferedFields.remove(currentField.group), currentField); - } else { + emitRemodeledMultiscriptField(bufferedFields.remove(currentField.getGroup()), currentField); + } + else { bufferMultiscriptField(currentField); } } @@ -169,38 +177,39 @@ public void endEntity() { @Override public void literal(final String name, final String value) { - currentField.stream.literal(name, value); + currentField.getStream().literal(name, value); if (GROUP_SUBFIELD.equals(name)) { - currentField.group = value; - } else if (SCRIPT_SUBFIELD.equals(name)) { - currentField.script = value; + currentField.setGroup(value); + } + else if (SCRIPT_SUBFIELD.equals(name)) { + currentField.setScript(value); } } private void bufferMultiscriptField(final BufferedField field) { - bufferedFields.put(field.group, field); + bufferedFields.put(field.getGroup(), field); } private Map getSingleMultiscriptFieldsBeforeCurrentField() { - return bufferedFields.headMap(currentField.group); + return bufferedFields.headMap(currentField.getGroup()); } private void emitNonMultiscriptField() { - getReceiver().startEntity(currentField.name); - currentField.stream.replay(); + getReceiver().startEntity(currentField.getName()); + currentField.getStream().replay(); getReceiver().endEntity(); } private void emitRemodeledMultiscriptField(final BufferedField firstField, final BufferedField secondField) { - getReceiver().startEntity(firstField.name); + getReceiver().startEntity(firstField.getName()); - getReceiver().startEntity(mapScriptToEntityName(firstField.script)); - firstField.stream.replay(); + getReceiver().startEntity(mapScriptToEntityName(firstField.getScript())); + firstField.getStream().replay(); getReceiver().endEntity(); - getReceiver().startEntity(mapScriptToEntityName(secondField.script)); - secondField.stream.replay(); + getReceiver().startEntity(mapScriptToEntityName(secondField.getScript())); + secondField.getStream().replay(); getReceiver().endEntity(); getReceiver().endEntity(); @@ -208,42 +217,62 @@ private void emitRemodeledMultiscriptField(final BufferedField firstField, final private void emitAsSingleMultiscriptFields(final Map fields) { for (final BufferedField field : fields.values()) { - getReceiver().startEntity(field.name); - field.stream.replay(); + getReceiver().startEntity(field.getName()); + field.getStream().replay(); getReceiver().endEntity(); } fields.clear(); } private String mapScriptToEntityName(final String script) { - if (LATIN_SCRIPT.equals(script)) { - return ENTITY_NAME_FOR_LATIN; - } else if (ARABIC_SCRIPT.equals(script) - || HEBREW_SCRIPT.equals(script)) { - return ENTITY_NAME_FOR_NON_LATIN_RL; - } - return ENTITY_NAME_FOR_NON_LATIN_LR; + return LATIN_SCRIPT.equals(script) ? ENTITY_NAME_FOR_LATIN : + (ARABIC_SCRIPT.equals(script) || HEBREW_SCRIPT.equals(script)) ? ENTITY_NAME_FOR_NON_LATIN_RL : + ENTITY_NAME_FOR_NON_LATIN_LR; } private static class BufferedField { - public String group; - public String script; + private final String name; + private final StreamBuffer stream; - public final String name; - public final StreamBuffer stream; + private String group; + private String script; - public BufferedField(final String name) { + BufferedField(final String name) { this(name, new StreamBuffer()); } - public BufferedField(final String name, final StreamBuffer stream) { + BufferedField(final String name, final StreamBuffer stream) { this.group = null; this.script = null; this.name = name; this.stream = stream; } + public String getName() { + return name; + } + + public StreamBuffer getStream() { + return stream; + } + + public void setGroup(final String group) { + this.group = group; + } + + public String getGroup() { + return group; + } + + public void setScript(final String script) { + this.script = script; + } + + public String getScript() { + return script; + } + } } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaParserContext.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaParserContext.java index 08f2edaf3..13e2e21f2 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaParserContext.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaParserContext.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.pica; +import org.metafacture.framework.StreamReceiver; + import java.text.Normalizer; import java.text.Normalizer.Form; -import org.metafacture.framework.StreamReceiver; - /** * Parser context for the PICA+ parser.The context implements * support for normalising the UTF8 encoding of values into NFC @@ -43,6 +44,9 @@ final class PicaParserContext { private String subfieldName; + PicaParserContext() { + } + public void setNormalizeUTF8(final boolean normalizeUTF8) { this.normalizeUTF8 = normalizeUTF8; } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaParserState.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaParserState.java index b99be0ddb..44fcd590b 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaParserState.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaParserState.java @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.pica; +package org.metafacture.biblio.pica; /** * A parser for PICA+ records. Only single records can be parsed as the parser @@ -37,23 +37,23 @@ enum PicaParserState { FIELD_NAME { @Override - protected PicaParserState parseChar(final char ch, final PicaParserContext ctx, boolean normalized) { + protected PicaParserState parseChar(final char ch, final PicaParserContext ctx, final boolean normalized) { final PicaParserState next; switch (PicaConstants.from(normalized, ch)) { - case RECORD_MARKER: - case FIELD_MARKER: - case FIELD_END_MARKER: - ctx.emitStartEntity(); - ctx.emitEndEntity(); - next = FIELD_NAME; - break; - case SUBFIELD_MARKER: - ctx.emitStartEntity(); - next = SUBFIELD_NAME; - break; - default: - ctx.appendText(ch); - next = this; + case RECORD_MARKER: + case FIELD_MARKER: + case FIELD_END_MARKER: + ctx.emitStartEntity(); + ctx.emitEndEntity(); + next = FIELD_NAME; + break; + case SUBFIELD_MARKER: + ctx.emitStartEntity(); + next = SUBFIELD_NAME; + break; + default: + ctx.appendText(ch); + next = this; } return next; } @@ -66,21 +66,21 @@ protected void endOfInput(final PicaParserContext ctx) { }, SUBFIELD_NAME { @Override - protected PicaParserState parseChar(final char ch, final PicaParserContext ctx, boolean normalized) { + protected PicaParserState parseChar(final char ch, final PicaParserContext ctx, final boolean normalized) { final PicaParserState next; switch (PicaConstants.from(normalized, ch)) { - case RECORD_MARKER: - case FIELD_MARKER: - case FIELD_END_MARKER: - ctx.emitEndEntity(); - next = FIELD_NAME; - break; - case SUBFIELD_MARKER: - next = this; - break; - default: - ctx.setSubfieldName(ch); - next = SUBFIELD_VALUE; + case RECORD_MARKER: + case FIELD_MARKER: + case FIELD_END_MARKER: + ctx.emitEndEntity(); + next = FIELD_NAME; + break; + case SUBFIELD_MARKER: + next = this; + break; + default: + ctx.setSubfieldName(ch); + next = SUBFIELD_VALUE; } return next; } @@ -92,23 +92,23 @@ protected void endOfInput(final PicaParserContext ctx) { }, SUBFIELD_VALUE { @Override - protected PicaParserState parseChar(final char ch, final PicaParserContext ctx, boolean normalized) { + protected PicaParserState parseChar(final char ch, final PicaParserContext ctx, final boolean normalized) { final PicaParserState next; switch (PicaConstants.from(normalized, ch)) { - case RECORD_MARKER: - case FIELD_MARKER: - case FIELD_END_MARKER: - ctx.emitLiteral(); - ctx.emitEndEntity(); - next = FIELD_NAME; - break; - case SUBFIELD_MARKER: - ctx.emitLiteral(); - next = SUBFIELD_NAME; - break; - default: - ctx.appendText(ch); - next = this; + case RECORD_MARKER: + case FIELD_MARKER: + case FIELD_END_MARKER: + ctx.emitLiteral(); + ctx.emitEndEntity(); + next = FIELD_NAME; + break; + case SUBFIELD_MARKER: + ctx.emitLiteral(); + next = SUBFIELD_NAME; + break; + default: + ctx.appendText(ch); + next = this; } return next; } @@ -120,8 +120,8 @@ protected void endOfInput(final PicaParserContext ctx) { } }; - protected abstract PicaParserState parseChar(final char ch, final PicaParserContext ctx, final boolean normalized); + protected abstract PicaParserState parseChar(char ch, PicaParserContext ctx, boolean normalized); - protected abstract void endOfInput(final PicaParserContext ctx); + protected abstract void endOfInput(PicaParserContext ctx); } diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaXmlHandler.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaXmlHandler.java index 129fa82f8..4435aa4fa 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaXmlHandler.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/pica/PicaXmlHandler.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.biblio.pica; -import java.text.Normalizer; +package org.metafacture.biblio.pica; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.StreamReceiver; @@ -24,9 +23,12 @@ import org.metafacture.framework.annotations.In; import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultXmlPipe; + import org.xml.sax.Attributes; import org.xml.sax.SAXException; +import java.text.Normalizer; + /** * A pica xml handler. * @@ -48,18 +50,27 @@ public final class PicaXmlHandler extends DefaultXmlPipe { private String currentTag = ""; private StringBuilder builder = new StringBuilder(); + /** + * Creates an instance of {@link PicaXmlHandler}. + */ + public PicaXmlHandler() { + } + @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { if (SUBFIELD.equals(localName)) { builder = new StringBuilder(); currentTag = attributes.getValue("id"); - } else if (DATAFIELD.equals(localName)) { + } + else if (DATAFIELD.equals(localName)) { getReceiver().startEntity( attributes.getValue("id") + attributes.getValue("occ")); - } else if (RECORD.equals(localName) && NAMESPACE.equals(uri)) { + } + else if (RECORD.equals(localName) && NAMESPACE.equals(uri)) { getReceiver().startRecord(""); - } else if (LEADER.equals(localName)) { + } + else if (LEADER.equals(localName)) { builder = new StringBuilder(); currentTag = LEADER; } @@ -71,9 +82,11 @@ public void endElement(final String uri, final String localName, if (SUBFIELD.equals(localName)) { getReceiver().literal(currentTag, Normalizer.normalize(builder.toString().trim(), Normalizer.Form.NFC)); - } else if (DATAFIELD.equals(localName)) { + } + else if (DATAFIELD.equals(localName)) { getReceiver().endEntity(); - } else if (RECORD.equals(localName) && NAMESPACE.equals(uri)) { + } + else if (RECORD.equals(localName) && NAMESPACE.equals(uri)) { getReceiver().endRecord(); } } diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/AlephMabXmlHandlerTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/AlephMabXmlHandlerTest.java index 7fb1bd542..418e8e9b5 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/AlephMabXmlHandlerTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/AlephMabXmlHandlerTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio; import org.junit.Before; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/AseqDecoderTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/AseqDecoderTest.java index b6359d8a0..ac74bf3d2 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/AseqDecoderTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/AseqDecoderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio; import static org.mockito.Mockito.inOrder; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/ComarcXmlHandlerTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/ComarcXmlHandlerTest.java index 7eefb4a85..6f8ba61a5 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/ComarcXmlHandlerTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/ComarcXmlHandlerTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio; import static org.mockito.Mockito.verify; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/MabDecoderTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/MabDecoderTest.java index e74fb29d4..51d477c9b 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/MabDecoderTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/MabDecoderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio; import static org.mockito.Mockito.inOrder; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/DirectoryEntryTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/DirectoryEntryTest.java index 1ee116021..acfe63097 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/DirectoryEntryTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/DirectoryEntryTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; import static org.junit.Assert.assertArrayEquals; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/Iso646ByteBufferTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/Iso646ByteBufferTest.java index 0c56d6382..d575429e1 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/Iso646ByteBufferTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/Iso646ByteBufferTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; import static org.junit.Assert.assertArrayEquals; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/LabelTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/LabelTest.java index 773d5e6b0..7697f59d6 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/LabelTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/LabelTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; import static org.junit.Assert.assertArrayEquals; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordBuilderTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordBuilderTest.java index 7117a691c..ec8c3d3f2 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordBuilderTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordBuilderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; import static org.junit.Assert.assertEquals; @@ -544,7 +545,8 @@ public void shouldLeaveRecordInACleanStateIfAppendingDataFieldFailed() { builder.appendSubfield(asChars("B"), "Value"); try { builder.endDataField(); - } catch (final FormatException e) { + } + catch (final FormatException e) { exceptionThrown = true; } diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordFormatTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordFormatTest.java index b3f78005b..51cc95d9e 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordFormatTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordFormatTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; import static org.junit.Assert.assertEquals; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordTest.java index b6d76b42b..a3722f116 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/iso2709/RecordTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.iso2709; import static org.junit.Assert.assertEquals; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/Marc21DecoderTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/Marc21DecoderTest.java index c65be8626..e983c9973 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/Marc21DecoderTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/Marc21DecoderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.marc21; import static org.mockito.Mockito.inOrder; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/Marc21EncoderTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/Marc21EncoderTest.java index 8317581ad..41e8d2b6c 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/Marc21EncoderTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/Marc21EncoderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.marc21; import static org.metafacture.biblio.marc21.Marc21EventNames.LEADER_ENTITY; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlEncoderTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlEncoderTest.java index 5f4d90e01..bc6fb0d49 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlEncoderTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlEncoderTest.java @@ -34,8 +34,7 @@ */ public class MarcXmlEncoderTest { - private static StringBuilder resultCollector; - private static MarcXmlEncoder encoder; + private static final String XML_DECLARATION = ""; private static final String XML_1_DECLARATION = ""; private static final String XML_16_DECLARATION = ""; @@ -48,6 +47,9 @@ public class MarcXmlEncoderTest { private static final String XML_MARC_COLLECTION_END_TAG = ""; private static final String RECORD_ID = "92005291"; + private static StringBuilder resultCollector; + private static MarcXmlEncoder encoder; + @Before public void setUp() { encoder = new MarcXmlEncoder(); @@ -166,6 +168,18 @@ public void createTwoRecordsInOneCollection() { assertEquals(expected, actual); } + @Test + public void issue403_shouldNotEmitNamespaceIfDisabled() { + encoder.setEmitNamespace(false); + addOneRecord(encoder); + addOneRecord(encoder); + encoder.closeStream(); + String expected = XML_DECLARATION + "" + + XML_RECORD + XML_RECORD + XML_MARC_COLLECTION_END_TAG; + String actual = resultCollector.toString(); + assertEquals(expected.replace("marc:", ""), actual); + } + @Test(expected = MetafactureException.class) public void emitExceptionWhenEntityLengthNot5() { encoder.startRecord(RECORD_ID); @@ -186,6 +200,18 @@ public void createAnRecordWithLeader() { assertEquals(expected, actual); } + @Test + public void issue336_createRecordWithTopLevelLeader() { + encoder.startRecord("1"); + encoder.literal(Marc21EventNames.LEADER_ENTITY, "dummy"); + encoder.endRecord(); + encoder.closeStream(); + String expected = XML_DECLARATION + XML_ROOT_OPEN + + "dummy" + XML_MARC_COLLECTION_END_TAG; + String actual = resultCollector.toString(); + assertEquals(expected, actual); + } + @Test public void sendDataAndClearWhenRecordStartedAndStreamResets() { encoder.startRecord("1"); @@ -207,14 +233,60 @@ public void sendAndClearDataWhenOnResetStream() { @Test public void shouldIgnoreNullValueOfLiteral() { + encoder.startRecord(RECORD_ID); + encoder.literal("data", null); + encoder.endRecord(); + encoder.closeStream(); + String expected = XML_DECLARATION + XML_ROOT_OPEN + + "" + + XML_MARC_COLLECTION_END_TAG; + String actual = resultCollector.toString(); + assertEquals(expected, actual); + } + + @Test + public void shouldIgnoreNullValueOfTypeLiteral() { encoder.startRecord(RECORD_ID); encoder.literal("type", null); encoder.endRecord(); encoder.closeStream(); String expected = XML_DECLARATION + XML_ROOT_OPEN - + "" + + "" + + XML_MARC_COLLECTION_END_TAG; + String actual = resultCollector.toString(); + assertEquals(expected, actual); + } + + @Test + public void issue402_shouldEncodeTypeLiteralAsAttribute() { + encoder.startRecord(RECORD_ID); + encoder.literal("type", "value"); + encoder.endRecord(); + encoder.closeStream(); + String expected = XML_DECLARATION + XML_ROOT_OPEN + + "" + + XML_MARC_COLLECTION_END_TAG; + String actual = resultCollector.toString(); + assertEquals(expected, actual); + } + + @Test + public void shouldNotEncodeNestedTypeLiteralAsAttribute() { + encoder.startRecord(RECORD_ID); + encoder.startEntity("tag12"); + encoder.literal("type", "value"); + encoder.endEntity(); + encoder.endRecord(); + encoder.closeStream(); + String expected = XML_DECLARATION + XML_ROOT_OPEN + + "" + + "" + + "value" + + "" + + "" + XML_MARC_COLLECTION_END_TAG; String actual = resultCollector.toString(); assertEquals(expected, actual); } + } diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlHandlerTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlHandlerTest.java index e74d38827..8a9f8c869 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlHandlerTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlHandlerTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.marc21; import static org.mockito.Mockito.verify; @@ -22,7 +23,9 @@ import org.junit.Before; import org.junit.Test; import org.metafacture.framework.StreamReceiver; +import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; @@ -74,6 +77,20 @@ public void shouldFindTagAttributeAtSecondPositionInControlFieldElement() verify(receiver).literal("001", fieldValue); } + @Test + public void issue440_shouldNotRemoveWhitespaceFromControlFields() throws SAXException { + final AttributesImpl attributes = new AttributesImpl(); + attributes.addAttribute(NAMESPACE, "tag", "tag", "CDATA", "008"); + + final String fieldValue = " t20202020au |||||||||||| ||||ger d"; + + marcXmlHandler.startElement(NAMESPACE, CONTROLFIELD, "", attributes); + marcXmlHandler.characters(fieldValue.toCharArray(), 0, fieldValue.length()); + marcXmlHandler.endElement(NAMESPACE, CONTROLFIELD, ""); + + verify(receiver).literal("008", fieldValue); + } + @Test public void issue233ShouldNotRemoveWhitespaceFromLeader() throws SAXException { @@ -129,4 +146,39 @@ public void issue330ShouldOptionallyRecognizeRecordsWithoutNamespace() verifyNoMoreInteractions(receiver); } + @Test + public void shouldNotEncodeTypeAttributeAsMarkedLiteral() throws SAXException { + final AttributesImpl attributes = new AttributesImpl(); + attributes.addAttribute(NAMESPACE, "type", "type", "CDATA", "bibliographic"); + + marcXmlHandler.startElement(NAMESPACE, RECORD, "", attributes); + marcXmlHandler.endElement(NAMESPACE, RECORD, ""); + + final InOrder ordered = Mockito.inOrder(receiver); + ordered.verify(receiver).startRecord(""); + ordered.verify(receiver).literal(TYPE, "bibliographic"); + ordered.verify(receiver).endRecord(); + ordered.verifyNoMoreInteractions(); + verifyNoMoreInteractions(receiver); + } + + @Test + public void issue336_shouldEncodeTypeAttributeAsLiteralWithConfiguredMarker() throws SAXException { + final String marker = "~"; + marcXmlHandler.setAttributeMarker(marker); + + final AttributesImpl attributes = new AttributesImpl(); + attributes.addAttribute(NAMESPACE, "type", "type", "CDATA", "bibliographic"); + + marcXmlHandler.startElement(NAMESPACE, RECORD, "", attributes); + marcXmlHandler.endElement(NAMESPACE, RECORD, ""); + + final InOrder ordered = Mockito.inOrder(receiver); + ordered.verify(receiver).startRecord(""); + ordered.verify(receiver).literal(marker + TYPE, "bibliographic"); + ordered.verify(receiver).endRecord(); + ordered.verifyNoMoreInteractions(); + verifyNoMoreInteractions(receiver); + } + } diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/pica/PicaEncoderTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/pica/PicaEncoderTest.java index a6cd3c058..2c83e392f 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/pica/PicaEncoderTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/pica/PicaEncoderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.pica; import static org.mockito.Mockito.verify; diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/pica/PicaMultiscriptRemodelerTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/pica/PicaMultiscriptRemodelerTest.java index 7dca1e449..be0f8a48a 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/pica/PicaMultiscriptRemodelerTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/pica/PicaMultiscriptRemodelerTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.biblio.pica; import static org.mockito.Mockito.inOrder; @@ -305,7 +306,8 @@ private void verifySingleMultiscriptField(final InOrder ordered, private String mapScriptToEntityName(final String script) { if (SCRIPT_LATIN.equals(script)) { return PicaMultiscriptRemodeler.ENTITY_NAME_FOR_LATIN; - } else if (SCRIPT_ARABIC.equals(script) + } + else if (SCRIPT_ARABIC.equals(script) || SCRIPT_HEBREW.equals(script)) { return PicaMultiscriptRemodeler.ENTITY_NAME_FOR_NON_LATIN_RL; } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/Require.java b/metafacture-commons/src/main/java/org/metafacture/commons/Require.java index b6b1c6840..d01f64a5a 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/Require.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/Require.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons; /** diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/ResourceUtil.java b/metafacture-commons/src/main/java/org/metafacture/commons/ResourceUtil.java index 97d171962..d803f0470 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/ResourceUtil.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/ResourceUtil.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons; import java.io.BufferedReader; @@ -30,7 +31,6 @@ import java.util.List; import java.util.Properties; - /** * Various utility methods for working with files, resources and streams. * @@ -38,7 +38,7 @@ * @author Markus Michael Geipel * */ -public final class ResourceUtil { +public final class ResourceUtil { // checkstyle-disable-line ClassDataAbstractionCoupling static final int BUFFER_SIZE = 4096; @@ -47,12 +47,12 @@ private ResourceUtil() { } /** - * First attempts to open open {@code name} as a file. On fail attempts to - * open resource with name {@code name}. On fail attempts to open {@code name} - * as a URL. + * First attempts to open a file with the provided name. On fail attempts to + * open a resource identified by the name. On fail attempts to open a URL + * identified by the name. * - * @param name name of the file or resource to open - * @return an input stream for reading the opened file or resource + * @param name name of the file, resource or the URL to open + * @return an input stream for reading the opened file, resource or URL * @throws FileNotFoundException if all attempts fail */ public static InputStream getStream(final String name) @@ -67,23 +67,34 @@ public static InputStream getStream(final String name) InputStream stream = Thread.currentThread().getContextClassLoader() .getResourceAsStream(name); - if (stream != null) { - return stream; - } - - try { - stream = new URL(name).openStream(); - } catch (final IOException e) { - throwFileNotFoundException(name, e); - } if (stream == null) { - throwFileNotFoundException(name, null); + try { + stream = new URL(name).openStream(); + } + catch (final IOException e) { + throwFileNotFoundException(name, e); + } + if (stream == null) { + throwFileNotFoundException(name, null); + } } return stream; } + /** + * Gets an InputStream of a File. + * + * @param file the File. + * @return the InputStream + * @throws FileNotFoundException if the File couldn't be found + */ + public static InputStream getStream(final File file) + throws FileNotFoundException { + return new FileInputStream(file); + } + private static void throwFileNotFoundException(final String name, final Throwable t) throws FileNotFoundException { final FileNotFoundException e = new FileNotFoundException( @@ -94,25 +105,53 @@ private static void throwFileNotFoundException(final String name, throw e; } - public static InputStream getStream(final File file) - throws FileNotFoundException { - return new FileInputStream(file); - } - + /** + * Gets a Reader. First attempts to open a file. On fail attempts to open the + * resource with name. On fail attempts to open name as a URL. + * + * @param name the name of the resource + * @return the Reader + * @throws FileNotFoundException if the File couldn't be found + */ public static Reader getReader(final String name) throws FileNotFoundException { return new InputStreamReader(getStream(name)); } + /** + * Gets a Reader from a File. + * + * @param file the File + * @return the Reader + * @throws FileNotFoundException if the File couldn't be found + */ public static Reader getReader(final File file) throws FileNotFoundException { return new InputStreamReader(getStream(file)); } + /** + * Gets a Reader. First attempts to open a file. On fail attempts to open the + * resource with name. On fail attempts to open name as a URL. Uses the given + * {@link java.nio.charset.Charset charset} as encoding. + * + * @param name the name of the resource + * @param encoding the Charset + * @return the Reader + * @throws IOException if an I/O error occurs + */ public static Reader getReader(final String name, final String encoding) throws IOException { return new InputStreamReader(getStream(name), encoding); } + /** + * Gets a Reader from a File using {@link java.nio.charset.Charset charset}. + * + * @param file the File + * @param encoding the Charset + * @return the Reader + * @throws IOException if an I/O error occurs + */ public static Reader getReader(final File file, final String encoding) throws IOException { return new InputStreamReader(getStream(file), encoding); @@ -137,22 +176,41 @@ public static URL getUrl(final String name) throws MalformedURLException { final URL resourceUrl = Thread.currentThread().getContextClassLoader().getResource(name); - if (resourceUrl != null) { - return resourceUrl; - } - - return new URL(name); + return resourceUrl != null ? resourceUrl : new URL(name); } + /** + * Gets an URL of a File. + * + * @param file the File + * @return the URL + * @throws MalformedURLException if malformed URL has occurred + */ public static URL getUrl(final File file) throws MalformedURLException { return file.toURI().toURL(); } + /** + * Creates Properties based upon a location. First attempts to open a file. On + * fail attempts to open the resource with name. On fail attempts to open name + * as a URL. + * + * @param location the location of the resource + * @return the Properties + * @throws IOException if an I/O error occurs + */ public static Properties loadProperties(final String location) throws IOException { return loadProperties(getStream(location)); } + /** + * Loads properties from an InputStream. + * + * @param stream properties as InputStream + * @return the Properties + * @throws IOException if an I/O error occurs + */ public static Properties loadProperties(final InputStream stream) throws IOException { final Properties properties; @@ -161,10 +219,24 @@ public static Properties loadProperties(final InputStream stream) return properties; } + /** + * Loads properties from a URL. + * + * @param url properties as URL + * @return the Properties + * @throws IOException if an I/O error occurs + */ public static Properties loadProperties(final URL url) throws IOException { return loadProperties(url.openStream()); } + /** + * Loads a text file. + * + * @param location the filename + * @return the content of the file + * @throws IOException if an I/O error occurs + */ public static String loadTextFile(final String location) throws IOException { final StringBuilder builder = new StringBuilder(); final BufferedReader reader = new BufferedReader(getReader(location)); @@ -178,6 +250,14 @@ public static String loadTextFile(final String location) throws IOException { return builder.toString(); } + /** + * * Loads a text file. + * + * @param location the filename + * @param list a List of Strings to append the lines of the file to + * @return the List of Strings with the lines of the file appended + * @throws IOException if an I/O error occurs + */ public static List loadTextFile(final String location, final List list) throws IOException { final BufferedReader reader = new BufferedReader(getReader(location)); @@ -191,14 +271,29 @@ public static List loadTextFile(final String location, return list; } - public static String readAll(InputStream inputStream, Charset encoding) + /** + * Reads an InputStream with the given Charset. + * + * @param inputStream the InputStream + * @param encoding the Charset + * @return a String of the content of the InputStream + * @throws IOException if an I/O error occurs + */ + public static String readAll(final InputStream inputStream, final Charset encoding) throws IOException { try (Reader reader = new InputStreamReader(inputStream, encoding)) { return readAll(reader); } } - public static String readAll(Reader reader) throws IOException { + /** + * Reads a Reader. + * + * @param reader the Reader + * @return a String of the content of the Reader + * @throws IOException if an I/O error occurs + */ + public static String readAll(final Reader reader) throws IOException { final StringBuilder loadedText = new StringBuilder(); try (Reader bufferedReader = new BufferedReader(reader)) { final CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE); diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/StringUtil.java b/metafacture-commons/src/main/java/org/metafacture/commons/StringUtil.java index 3880442f9..ce9c7f89c 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/StringUtil.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/StringUtil.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons; import java.nio.CharBuffer; @@ -26,13 +27,21 @@ */ public final class StringUtil { - private static final String DEFAULT_VARSTART = "${"; - private static final String DEFAULT_VAREND = "}"; + public static final String DEFAULT_VARSTART = "${"; + public static final String DEFAULT_VAREND = "}"; private StringUtil() { // no instances allowed } + /** + * Returns a fallback of an object if the object is null. + * + * @param the type of the object + * @param value the object + * @param fallbackValue the default object + * @return an object + */ public static O fallback(final O value, final O fallbackValue) { if (value == null) { return fallbackValue; @@ -40,20 +49,64 @@ public static O fallback(final O value, final O fallbackValue) { return value; } + /** + * Formats a String. If a String has a variable it will be replaced based on a + * Map. The start and the end of indicating this variable must be defined. + * {@value #DEFAULT_VARSTART} indicates the start of a variable and + * {@value #DEFAULT_VAREND} the end of the variable . Unassigned variables are + * ignored. + * + * @param format the String to be formatted + * @param variables a Map of variable names and their values + * @return the formatted String + */ public static String format(final String format, final Map variables) { return format(format, DEFAULT_VARSTART, DEFAULT_VAREND, true, variables); } + /** + * Formats a String. If a String has a variable it will be replaced based on a + * Map. The start and the end of indicating this variable must be defined. + * Unassigned variables are ignored. + * + * @param format the String to be formatted + * @param varStartIndicator a String indicating the start of a variable + * @param varEndIndicator a String indicating the end of a variable + * @param variables a Map of variable names and their values + * @return the formatted String + */ public static String format(final String format, final String varStartIndicator, final String varEndIndicator, final Map variables) { return format(format, varStartIndicator, varEndIndicator, true, variables); } + /** + * Formats a String. If a String has a variable it will be replaced based on a + * Map. The start and the end of indicating this variable must be defined. + * {@value #DEFAULT_VARSTART} indicates the start of a variable and + * {@value #DEFAULT_VAREND} the end of the variable . + * + * @param format the String to be formatted + * @param ignoreMissingVars boolean if an unassigned variable should be ignored + * @param variables a Map of variable names and their values + * @return the formatted String + */ public static String format(final String format, final boolean ignoreMissingVars, final Map variables) { return format(format, DEFAULT_VARSTART, DEFAULT_VAREND, ignoreMissingVars, variables); } + /** + * Formats a String. If a String has a variable it will be replaced based on a + * Map. The start and the end of indicating this variable must be defined. + * + * @param format the String to be formatted + * @param varStartIndicator a String indicating the start of a variable + * @param varEndIndicator a String indicating the end of a variable + * @param ignoreMissingVars boolean if an unassigned variable should be ignored + * @param variables a Map of variable names and their values + * @return the formatted String + */ public static String format(final String format, final String varStartIndicator, final String varEndIndicator, final boolean ignoreMissingVars, final Map variables) { if (format.indexOf(varStartIndicator) < 0) { // shortcut if there is @@ -85,9 +138,10 @@ public static String format(final String format, final String varStartIndicator, if (varValue == null) { if (ignoreMissingVars) { varValue = ""; - } else { - throw new IllegalArgumentException("Variable '" + varName - + "' was not assigned!\nAssigned variables:\n" + variables); + } + else { + throw new IllegalArgumentException("Variable '" + varName + + "' was not assigned!\nAssigned variables:\n" + variables); } } builder.append(varValue); @@ -135,7 +189,7 @@ public static char[] copyToBuffer(final String str, final char[] currentBuffer) char[] buffer = currentBuffer; int bufferLen = buffer.length; - while(strLen > bufferLen) { + while (strLen > bufferLen) { bufferLen *= 2; } if (bufferLen > buffer.length) { diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/TimeUtil.java b/metafacture-commons/src/main/java/org/metafacture/commons/TimeUtil.java index 41109d6a5..55b274543 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/TimeUtil.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/TimeUtil.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons; /** @@ -22,8 +23,8 @@ */ public final class TimeUtil { - public static final String[] UNIT_SYMBOLS = { "ns", "µs", "ms", "s", "min", "h" }; - public static final long[] UNIT_FACTORS = { 1L, 1000L, 1000L, 1000L, 60L, 60L }; + public static final String[] UNIT_SYMBOLS = {"ns", "µs", "ms", "s", "min", "h"}; + public static final long[] UNIT_FACTORS = {1L, 1000L, 1000L, 1000L, 60L, 60L}; public static final int BASE_UNIT_INDEX = 3; @@ -38,6 +39,12 @@ private TimeUtil() { // No instances allowed } + /** + * Formats a duration to human readable abbrevations. + * + * @param duration a long value of the duration + * @return a human readable format of the duration + */ public static String formatDuration(final long duration) { long major = duration; long minor = 0; diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/XmlUtil.java b/metafacture-commons/src/main/java/org/metafacture/commons/XmlUtil.java index 1b8ffb270..c56d41757 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/XmlUtil.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/XmlUtil.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons; -import static java.util.stream.Collectors.joining; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import java.io.StringWriter; - +import java.util.stream.Collectors; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; @@ -26,10 +28,6 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - - /** * Utility functions for working with XML data as strings. * @@ -41,34 +39,52 @@ public final class XmlUtil { private static final String TEXT_XML_MIME_TYPE = "text/xml"; private static final String XML_BASE_MIME_TYPE = "+xml"; + private static final int ESCAPE_CODE_POINT_THRESHOLD = 0x7f; + private XmlUtil() { // No instances allowed } + /** + * Converts a Node to a String. + * + * @param node the Node + * @return the String represantation of the Node + */ public static String nodeToString(final Node node) { return nodeToString(node, false); } + /** + * Converts a Node to a String, with or without an XML declaration + * + * @param node the Node + * @param omitXMLDecl boolean if an XML declaration is omitted or not + * @return a String representation of the Node + */ public static String nodeToString(final Node node, final boolean omitXMLDecl) { final StringWriter writer = new StringWriter(); final Transformer transformer; try { transformer = TransformerFactory.newInstance().newTransformer(); - } catch (final TransformerException e) { + } + catch (final TransformerException e) { throw new AssertionError( "No errors expected when creating an identity transformer", e); } if (omitXMLDecl) { transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - } else { + } + else { transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); } try { transformer.transform(new DOMSource(node), new StreamResult(writer)); - } catch (final TransformerException e) { + } + catch (final TransformerException e) { throw new AssertionError( "No errors expected during identity transformation", e); } @@ -76,16 +92,28 @@ public static String nodeToString(final Node node, return writer.toString(); } + /** + * Converts a NodeList to a String. + * + * @param nodes the NodeList + * @return a String representation of the NodeList + */ public static String nodeListToString(final NodeList nodes) { final StringBuilder builder = new StringBuilder(); - for (int i=0; i < nodes.getLength(); ++i) { + for (int i = 0; i < nodes.getLength(); ++i) { builder.append(nodeToString(nodes.item(i), i != 0)); } return builder.toString(); } + /** + * Checks if a String is an XML MIME type. + * + * @param mimeType the MIME type + * @return boolean if a String is an XML MIME type + */ public static boolean isXmlMimeType(final String mimeType) { if (mimeType == null) { return false; @@ -95,37 +123,63 @@ public static boolean isXmlMimeType(final String mimeType) { mimeType.endsWith(XML_BASE_MIME_TYPE); } + /** + * Escapes an unescaped String. + * + * @param unescaped the unescaped String + * @return the escaped String + */ public static String escape(final String unescaped) { return escape(unescaped, true); } + /** + * Escapes XML special characters. May also escape non-ASCII characters (aka + * Unicode). + * + * @param unescaped the String to be unescaped + * @param escapeUnicode boolean if Unicode should be also escaped + * @return the escaped String + */ public static String escape(final String unescaped, final boolean escapeUnicode) { return unescaped.codePoints() .mapToObj(value -> escapeCodePoint(value, escapeUnicode)) - .collect(joining()); + .collect(Collectors.joining()); } - + private static String escapeCodePoint(final int codePoint, final boolean escapeUnicode) { final String entity = entityFor(codePoint); if (entity != null) { return entity; } - if (escapeUnicode && codePoint > 0x7f) { - return "&#" + Integer.toString(codePoint) + ";"; - } - return Character.toString((char) codePoint); + return escapeUnicode && codePoint > ESCAPE_CODE_POINT_THRESHOLD ? + "&#" + Integer.toString(codePoint) + ";" : Character.toString((char) codePoint); } private static String entityFor(final int ch) { + final String entity; + switch (ch) { - case '<': return "<"; - case '>': return ">"; - case '&': return "&"; - case '"': return """; - case '\'': return "'"; + case '<': + entity = "<"; + break; + case '>': + entity = ">"; + break; + case '&': + entity = "&"; + break; + case '"': + entity = """; + break; + case '\'': + entity = "'"; + break; default: - return null; + entity = null; } + + return entity; } } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ConfigurableClass.java b/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ConfigurableClass.java index 6980dcf55..ab9b7c230 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ConfigurableClass.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ConfigurableClass.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.commons.reflection; -import static java.util.Arrays.asList; +package org.metafacture.commons.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -33,26 +33,43 @@ * Instances of this class wrap {@link Class}. The wrapped instance is available * via {@link #getPlainClass()}. * + * @param object type * @author Christoph Böhme */ public final class ConfigurableClass { private static final String SETTER_PREFIX = "set"; private static final Set> ELIGIBLE_TYPES = new HashSet<>( - asList(boolean.class, int.class, String.class)); + Arrays.asList(boolean.class, int.class, String.class)); private final Class plainClass; private Map settersCache; - public ConfigurableClass(Class plainClass) { + /** + * + * Creates an instance of {@link ConfigurableClass} defined by a Class. + * + * @param plainClass the plain class of object type T + */ + public ConfigurableClass(final Class plainClass) { this.plainClass = plainClass; } + /** + * Gets the plain class of the ConfigurableClass. + * + * @return the Class + */ public Class getPlainClass() { return plainClass; } + /** + * Gets all public "set" methods of this class. + * + * @return the Map of the setter methods of this class + */ public Map getSetters() { if (settersCache == null) { initSettersCache(); @@ -62,7 +79,7 @@ public Map getSetters() { private void initSettersCache() { settersCache = new HashMap<>(); - for (Method method : plainClass.getMethods()) { + for (final Method method : plainClass.getMethods()) { if (isSetter(method)) { final String setterName = method.getName().substring( SETTER_PREFIX.length()).toLowerCase(); @@ -71,7 +88,7 @@ private void initSettersCache() { } } - private boolean isSetter(Method method) { + private boolean isSetter(final Method method) { if (method.getParameterTypes().length == 1) { final Class type = method.getParameterTypes()[0]; if (ELIGIBLE_TYPES.contains(type) || type.isEnum()) { @@ -81,38 +98,64 @@ private boolean isSetter(Method method) { return false; } - public Map> getSetterTypes() { + /** + * Gets the parameter type of the setter method. + * + * @param method the setter method + * @return the type + */ + public Class getSetterType(final Method method) { + return method.getParameterTypes()[0]; + } + + /** + * Gets the parameter types of the setter methods. + * + * @return a Map of the setter method names and their types + */ + public Map> getSetterTypes() { final Map> setterTypes = new HashMap<>(); - for(Map.Entry method : getSetters().entrySet()) { - final Class setterType = method.getValue().getParameterTypes()[0]; - setterTypes.put(method.getKey(), setterType); + for (final Map.Entry entry : getSetters().entrySet()) { + setterTypes.put(entry.getKey(), getSetterType(entry.getValue())); } return setterTypes; } + /** + * Creates an empty instance of the class. + * + * @return a new instance + */ public T newInstance() { return newInstance(Collections.emptyMap()); } - public T newInstance(Map setterValues, - Object... constructorArgs) { + /** + * Creates an instance of the class using the first constructor that matches the + * varargs argument of the methods. + * + * @param setterValues the Map of setter values + * @param constructorArgs the Object of args of the constructor + * @return the new instance + */ + public T newInstance(final Map setterValues, final Object... constructorArgs) { try { final Constructor constructor = findConstructor(constructorArgs); final T instance = constructor.newInstance(constructorArgs); applySetters(instance, setterValues); return instance; - } catch (ReflectiveOperationException e) { + } + catch (final ReflectiveOperationException e) { throw new ReflectionException("class could not be instantiated: " + plainClass, e); } } - private Constructor findConstructor(Object... arguments) - throws NoSuchMethodException{ + private Constructor findConstructor(final Object... arguments) throws NoSuchMethodException { @SuppressWarnings("unchecked") // getConstructors() returns correct types final Constructor[] constructors = (Constructor[]) plainClass.getConstructors(); - for (Constructor constructor : constructors) { + for (final Constructor constructor : constructors) { if (checkArgumentTypes(constructor, arguments)) { return constructor; } @@ -121,8 +164,7 @@ private Constructor findConstructor(Object... arguments) "no appropriate constructor found for class " + plainClass); } - private boolean checkArgumentTypes(Constructor constructor, - Object[] constructorArgs) { + private boolean checkArgumentTypes(final Constructor constructor, final Object[] constructorArgs) { // checkstyle-disable-line ReturnCount final Class[] argTypes = constructor.getParameterTypes(); if (argTypes.length != constructorArgs.length) { return false; @@ -135,8 +177,8 @@ private boolean checkArgumentTypes(Constructor constructor, return true; } - private void applySetters(T target, Map setterValues) { - for (Map.Entry setterValue : setterValues.entrySet()) { + private void applySetters(final T target, final Map setterValues) { + for (final Map.Entry setterValue : setterValues.entrySet()) { final String setterName = setterValue.getKey().toLowerCase(); final Method setter = getSetters().get(setterName); if (setter == null) { @@ -147,26 +189,33 @@ private void applySetters(T target, Map setterValues) { final Object value = convertValue(setterValue.getValue(), valueType); try { setter.invoke(target, value); - } catch (ReflectiveOperationException e) { + } + catch (final ReflectiveOperationException e) { throw new ReflectionException("Cannot set " + setterName + " on class " + target.getClass().getSimpleName(), e); } } } - private Object convertValue(String value, Class type) { + private > Object convertValue(final String value, final Class type) { + final Object result; + if (type == boolean.class) { - return Boolean.valueOf(value); + result = Boolean.valueOf(value); } - if (type == int.class) { - return Integer.valueOf(value); + else if (type == int.class) { + result = Integer.valueOf(value); } - if (type.isEnum()) { + else if (type.isEnum()) { @SuppressWarnings("unchecked") // protected by type.isEnum() check - final Class enumType = (Class) type; - return Enum.valueOf(enumType, value.toUpperCase()); + final Class enumType = (Class) type; + result = Enum.valueOf(enumType, value.toUpperCase()); } - return value; + else { + result = value; + } + + return result; } } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ObjectFactory.java b/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ObjectFactory.java index 16cd2c9f6..f7cc5bd8f 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ObjectFactory.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ObjectFactory.java @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.reflection; import java.util.Collections; @@ -36,7 +37,19 @@ public class ObjectFactory { private final Map> classes = new HashMap<>(); - public final void loadClassesFromMap(Map classMap, Class baseType) { + /** + * Creates an instance of {@link ObjectFactory}. + */ + public ObjectFactory() { + } + + /** + * Loads classes from a map. + * + * @param classMap the map of classes + * @param baseType the object type of the classes + */ + public final void loadClassesFromMap(final Map classMap, final Class baseType) { final ClassLoader loader = ReflectionUtil.getContextClassLoader(); for (final Entry entry : classMap.entrySet()) { final String key = entry.getKey().toString(); @@ -45,21 +58,46 @@ public final void loadClassesFromMap(Map classMap, Class baseType) { } } - public final void registerClass(String key, Class objectClass) { + /** + * Registers a Class as a ConfigurableClass. + * + * @param key the key associcated with the Class + * @param objectClass the Class + */ + public final void registerClass(final String key, final Class objectClass) { registerClass(key, new ConfigurableClass<>(objectClass)); } - public final void registerClass(String key, - ConfigurableClass objectClass) { + /** + * Registers a ConfigurableClass. + * + * @param key the key associcated with the ConfigurableClass + * @param objectClass the ConfigurableClass + */ + public final void registerClass(final String key, final ConfigurableClass objectClass) { classes.put(key, objectClass); } - public final T newInstance(String key, Object... constructorArgs) { + /** + * Returns a new instance of a ConfigurableClass with no setters. + * + * @param key the name of the class + * @param constructorArgs the args of the constructor + * @return a new instance + */ + public final T newInstance(final String key, final Object... constructorArgs) { return newInstance(key, Collections.emptyMap(), constructorArgs); } - public final T newInstance(String key, Map values, - Object... constructorArgs) { + /** + * Returns a new instance of a ConfigurableClass. + * + * @param key the name of the class + * @param values the Map of Strings of the setters + * @param constructorArgs the args of the constructor + * @return a new instance + */ + public final T newInstance(final String key, final Map values, final Object... constructorArgs) { if (!classes.containsKey(key)) { throw new NoSuchElementException("no registered class for: " + key); } @@ -67,15 +105,32 @@ public final T newInstance(String key, Map values, return instanceClass.newInstance(values, constructorArgs); } - public final boolean containsKey(String key) { + /** + * Checks whether a ConfigurableClass is asscociated with a key. + * + * @param key the key + * @return true if the key is associcated with a ConfigurableClass + */ + public final boolean containsKey(final String key) { return classes.containsKey(key); } + /** + * Gets the key set of all {ConfigurableClass}es. + * + * @return all keys that identify the {ConfigurableClass}es + */ public final Set keySet() { return Collections.unmodifiableSet(classes.keySet()); } - public final ConfigurableClass get(String key) { + /** + * Gets a ConfigurableClass. + * + * @param key the key that identifies the ConfigurableClass + * @return the ConfigurableClass + */ + public final ConfigurableClass get(final String key) { return classes.get(key); } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ReflectionException.java b/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ReflectionException.java index ef6311532..0af42bfc5 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ReflectionException.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ReflectionException.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.reflection; /** @@ -20,11 +21,11 @@ */ public class ReflectionException extends RuntimeException { - ReflectionException(String message, Throwable cause) { + ReflectionException(final String message, final Throwable cause) { super(message, cause); } - ReflectionException(String message) { + ReflectionException(final String message) { super(message); } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ReflectionUtil.java b/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ReflectionUtil.java index bb3a12d72..0b09336c5 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ReflectionUtil.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/reflection/ReflectionUtil.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.reflection; /** @@ -27,6 +28,10 @@ private ReflectionUtil() { throw new AssertionError("No instances allowed"); } + /** + * @return the context ClassLoader for this thread, or null indicating the + * system class loader (or, failing that, the bootstrap class loader) + */ public static ClassLoader getContextClassLoader() { final ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { @@ -35,22 +40,37 @@ public static ClassLoader getContextClassLoader() { return loader; } - public static ConfigurableClass loadClass(String className, - Class baseType) { + /** + * Loads a Class. + * + * @param the object type + * @param className the name of the class + * @param baseType the object type of the class to be wrapped + * @return the ConfigurableClass + */ + public static ConfigurableClass loadClass(final String className, final Class baseType) { return loadClass(getContextClassLoader(), className, baseType); } - public static ConfigurableClass loadClass(ClassLoader loader, - String className, Class baseType) { + /** + * Wraps a Class in a ConfigurableClass. + * + * @param the object type of the ConfigurableClass + * @param loader the ClassLoader + * @param className the name of the class + * @param baseType the object type of the class to be wrapped + * @return the ConfigurableClass + */ + public static ConfigurableClass loadClass(final ClassLoader loader, final String className, final Class baseType) { final Class clazz; try { clazz = loader.loadClass(className); - } catch (ClassNotFoundException e) { + } + catch (final ClassNotFoundException e) { throw new ReflectionException("Class not found: " + className, e); } if (!baseType.isAssignableFrom(clazz)) { - throw new ReflectionException(className + " must extend or implement " + - baseType.getName()); + throw new ReflectionException(className + " must extend or implement " + baseType.getName()); } @SuppressWarnings("unchecked") // protected by isAssignableFrom check final Class castedClass = (Class) clazz; diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/tries/ACNode.java b/metafacture-commons/src/main/java/org/metafacture/commons/tries/ACNode.java index 85602e1fe..8b6f8b54d 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/tries/ACNode.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/tries/ACNode.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; import java.util.Collection; import java.util.Map.Entry; import java.util.Set; - /** * Node representing a character in a trie. * @@ -38,12 +38,12 @@ final class ACNode

    { this.depth = depth; } - ACNode

    addNext(final char key){ + ACNode

    addNext(final char key) { return addNext(key, null); } - ACNode

    addNext(final char key, final P value){ - final ACNode

    next = new ACNode

    (value, depth+1); + ACNode

    addNext(final char key, final P currentValue) { + final ACNode

    next = new ACNode

    (currentValue, depth + 1); links.put(key, next); return next; } @@ -52,14 +52,18 @@ void setValue(final P value) { this.value = value; } - P getValue(){ + P getValue() { return value; } - ACNode

    getNext(final char key){ + ACNode

    getNext(final char key) { return links.get(key); } + Collection> getNext() { + return links.values(); + } + ACNode

    getFailure() { return failure; } @@ -72,10 +76,6 @@ int getDepth() { return depth; } - Collection> getNext(){ - return links.values(); - } - Set>> getLinks() { return links.entrySet(); } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/tries/CharMap.java b/metafacture-commons/src/main/java/org/metafacture/commons/tries/CharMap.java index 1bc7c940b..4f45b9a2a 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/tries/CharMap.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/tries/CharMap.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; import java.util.Collection; @@ -38,7 +39,7 @@ final class CharMap implements Map { private int size; @SuppressWarnings("unchecked") - public CharMap() { + CharMap() { table = new Entry[INITIAL_CAPACITY]; } @@ -97,14 +98,15 @@ public void put(final char key, final V value) { ++size; } - public void put(final Entry[] table, final char key, final V value) { + public void put(final Entry[] currentTable, final char key, final V value) { final Entry newEntry = new Entry(key, value); - Entry entry = table[key % table.length]; + Entry entry = currentTable[key % currentTable.length]; if (entry == null) { - table[key % table.length] = newEntry; - } else { + currentTable[key % currentTable.length] = newEntry; + } + else { while (entry.getNext() != null) { if (entry.getKeyChar() == key) { throw new IllegalStateException("Key '" + key + "' already used"); @@ -120,7 +122,7 @@ private void expand() { @SuppressWarnings("unchecked") final Entry[] newTable = new Entry[newSize]; - for (Entry entry : table) { + for (final Entry entry : table) { Entry temp = entry; while (temp != null) { put(newTable, temp.getKeyChar(), temp.getValue()); @@ -174,7 +176,7 @@ public Collection values() { @Override public Set> entrySet() { - final Set> entries = new HashSet> (); + final Set> entries = new HashSet>(); for (int i = 0; i < table.length; ++i) { Entry entry = table[i]; while (entry != null) { @@ -223,9 +225,9 @@ public V getValue() { } @Override - public V setValue(final V value) { - final V old = this.value; - this.value = value; + public V setValue(final V newValue) { + final V old = value; + value = newValue; return old; } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/tries/SetMatcher.java b/metafacture-commons/src/main/java/org/metafacture/commons/tries/SetMatcher.java index 048fc5cd6..e77e16ff9 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/tries/SetMatcher.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/tries/SetMatcher.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; import java.io.PrintStream; @@ -32,6 +33,18 @@ public final class SetMatcher { private final ACNode root = new ACNode<>(null, 0); private boolean isPrepared; + /** + * Creates an instance of {@link SetMatcher}. + */ + public SetMatcher() { + } + + /** + * Adds a value for a key. + * + * @param key the key + * @param value the value + */ public void put(final String key, final T value) { if (isPrepared) { throw new IllegalStateException("keys cannot be added during matching."); @@ -50,13 +63,21 @@ public void put(final String key, final T value) { next = node.getNext(key.charAt(length - 1)); if (next == null) { next = node.addNext(key.charAt(length - 1), value); - } else if (next.getValue() == null) { + } + else if (next.getValue() == null) { next.setValue(value); - } else { + } + else { throw new IllegalStateException("Key '" + key + "' already in trie"); } } + /** + * Gets the List of Matches of a text. + * + * @param text the text + * @return List of Matches + */ public List> match(final String text) { if (!isPrepared) { prepare(); @@ -72,7 +93,8 @@ public List> match(final String text) { final ACNode next = node.getNext(text.charAt(index)); if (next != null) { node = next; - } else if (node != root) { + } + else if (node != root) { node = node.getFailure(); continue; } @@ -85,12 +107,12 @@ public List> match(final String text) { private void collectMatches(final ACNode node, final int index, final List> matches) { //direct hit or hit in chain of failure links? ACNode tempNode = node; - do{ + do { if (tempNode.getValue() != null) { matches.add(new Match(tempNode.getValue(), index - tempNode.getDepth(), tempNode.getDepth())); } tempNode = tempNode.getFailure(); - }while (tempNode != root); + } while (tempNode != root); } private void prepare() { @@ -98,7 +120,7 @@ private void prepare() { // prepare root root.setFailure(root); - for (ACNode child : root.getNext()) { + for (final ACNode child : root.getNext()) { child.setFailure(root); queue.add(child); } @@ -107,7 +129,7 @@ private void prepare() { final ACNode parent = queue.poll(); final ACNode parentFailure = parent.getFailure(); - for (Entry> link : parent.getLinks()) { + for (final Entry> link : parent.getLinks()) { final char key = link.getKey().charValue(); final ACNode child = link.getValue(); ACNode node = parentFailure; @@ -118,7 +140,8 @@ private void prepare() { if (node.getNext(key) == null) { child.setFailure(root); - } else { + } + else { child.setFailure(node.getNext(key)); } queue.add(child); @@ -127,10 +150,10 @@ private void prepare() { } /** - * Prints dot description of the automaton to out for visualization in - * GraphViz. Used for debugging and education. + * Prints dot description of the automaton to the PrintStream for + * visualization in GraphViz. Used for debugging and education. * - * @param out the stream t which the description is written + * @param out the stream to which the description is written */ public void printAutomaton(final PrintStream out) { out.println("digraph ahocorasick {"); @@ -141,14 +164,15 @@ public void printAutomaton(final PrintStream out) { private void printDebug(final PrintStream out, final ACNode node) { if (node.getValue() == null) { out.println(node.hashCode() + " [shape=point label=\"\"]"); - } else { + } + else { out.println(node.hashCode() + " [shape=circle style=filled label=\"\"]"); } if (node.getFailure() != root) { out.println(node.hashCode() + " -> " + node.getFailure().hashCode() + "[color=gray]"); } - for (Entry> link : node.getLinks()) { + for (final Entry> link : node.getLinks()) { out.println(node.hashCode() + " -> " + link.getValue().hashCode() + " [label=\"" + link.getKey() + "\"]"); printDebug(out, link.getValue()); } @@ -164,21 +188,42 @@ public static final class Match { private final int start; private final int length; + /** + * Constructs a Match. + * + * @param value the value + * @param start the position + * @param length the length + */ public Match(final T value, final int start, final int length) { - super(); this.value = value; this.start = start; this.length = length; } + /** + * Gets the value. + * + * @return the value + */ public T getValue() { return value; } + /** + * Gets the start position. + * + * @return the start position + */ public int getStart() { return start; } + /** + * Gets the length. + * + * @return the length + */ public int getLength() { return length; } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/tries/SetReplacer.java b/metafacture-commons/src/main/java/org/metafacture/commons/tries/SetReplacer.java index 1f0ae0d55..8cc2ffea9 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/tries/SetReplacer.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/tries/SetReplacer.java @@ -13,76 +13,80 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; +import org.metafacture.commons.tries.SetMatcher.Match; + import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; - -import org.metafacture.commons.tries.SetMatcher.Match; /** - * @author Markus Michael Geipel + * Replaces Strings by other Strings. * + * @author Markus Michael Geipel */ public final class SetReplacer { private final SetMatcher matcher = new SetMatcher(); - public void addReplacement(final String key, final String with) { - matcher.put(key, with); + /** + * Creates an instance of {@link SetReplacer}. + */ + public SetReplacer() { + } + + /** + * Adds a replacement of a String by another String. + * + * @param toReplace String to replace + * @param replacement String of replacement + */ + public void addReplacement(final String toReplace, final String replacement) { + matcher.put(toReplace, replacement); } + /** + * Adds replacements given as a Map of Strings. + * + * @param replacements the Map of Strings to be replaced + */ public void addReplacements(final Map replacements) { - for (Entry entry : replacements.entrySet()) { - addReplacement(entry.getKey(), entry.getValue()); + for (final String k : replacements.keySet()) { + addReplacement(k, replacements.get(k)); } } + /** + * Replaces the Strings defined with {@link #addReplacement(String, String)} in + * the text. + * + * @param text the text + * @return the text with the replacements + */ public String replaceIn(final String text) { final List> matches = matcher.match(text); final StringBuilder builder = new StringBuilder(); - int lastCut = 0; Collections.sort(matches, new Comparator>() { @Override public int compare(final Match o1, final Match o2) { - final int result; final int delta = o1.getStart() - o2.getStart(); - if (delta < 0) { - result = -1; - } else if (delta > 0) { - result = 1; - } else { - if (o1.getLength() > o2.getLength()) { - result = -1; - } else { - result = 1; - } - } - return result; + return delta < 0 ? -1 : delta > 0 ? 1 : o1.getLength() > o2.getLength() ? -1 : 1; } - }); - for (SetMatcher.Match match : matches) { - + for (final SetMatcher.Match match : matches) { if (match.getStart() < lastCut) { continue; } - - // System.out.println(match.getStart() + " "+ match.getValue() +" "+ - // match.getLength()); - builder.append(text.substring(lastCut, match.getStart())); builder.append(match.getValue()); - lastCut = match.getStart() + match.getLength(); } builder.append(text.substring(lastCut, text.length())); - return builder.toString(); } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/tries/SimpleRegexTrie.java b/metafacture-commons/src/main/java/org/metafacture/commons/tries/SimpleRegexTrie.java index 2284eeb87..8514c3f9b 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/tries/SimpleRegexTrie.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/tries/SimpleRegexTrie.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; import java.util.List; @@ -32,10 +33,12 @@ public class SimpleRegexTrie

    { // matches: `lit-[A]`, `lit-[AB]`, does not match: `a[].1`, `a[].1.b[].1` public static final String SIMPLE_CHARACTER_CLASS = ".*\\[[^\\[\\]]+\\].*"; - private final WildcardTrie

    trie; + private final WildcardTrie

    trie = new WildcardTrie

    (); + /** + * Creates an instance of {@link SimpleRegexTrie}. + */ public SimpleRegexTrie() { - trie = new WildcardTrie

    (); } /** @@ -49,16 +52,25 @@ public void put(final String keys, final P value) { if (keys.matches(SIMPLE_CHARACTER_CLASS)) { int charClassStart = keys.indexOf('[', 0); final int charClassEnd = keys.indexOf(']', 1); - String begin = keys.substring(0, charClassStart); - for (; charClassStart < charClassEnd - 1; charClassStart++) { - char middle = keys.charAt(charClassStart + 1); - String end = keys.substring(charClassEnd + 1, keys.length()); + final String begin = keys.substring(0, charClassStart); + for (; charClassStart < charClassEnd - 1; ++charClassStart) { + final char middle = keys.charAt(charClassStart + 1); + final String end = keys.substring(charClassEnd + 1, keys.length()); put(begin + middle + end, value); } - } else + } + else { trie.put(keys, value); + } } + /** + * Gets the List of values identified by a key. + * + * @see WildcardTrie + * @param key the key + * @return the List of the key if the key exists, otherwise an empty List + */ public List

    get(final String key) { return trie.get(key); } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/tries/SimpleTrie.java b/metafacture-commons/src/main/java/org/metafacture/commons/tries/SimpleTrie.java index a35f50e44..9494b11ed 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/tries/SimpleTrie.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/tries/SimpleTrie.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; /** @@ -24,32 +25,50 @@ public final class SimpleTrie

    { private final Node

    root = new Node<>(null); - public void put(final String key, final P value){ + /** + * Creates an instance of {@link SimpleTrie}. + */ + public SimpleTrie() { + } + /** + * Adds a value for the key. + * + * @param key the name of the key + * @param value the value + */ + public void put(final String key, final P value) { Node

    node = root; Node

    next; final int length = key.length(); - for (int i = 0; i < length-1; ++i) { + for (int i = 0; i < length - 1; ++i) { next = node.getNext(key.charAt(i)); - if(next==null){ + if (next == null) { next = node.addNext(key.charAt(i)); } node = next; } - next = node.getNext(key.charAt(length-1)); - if(next==null){ - next = node.addNext(key.charAt(length-1), value); - }else{ + next = node.getNext(key.charAt(length - 1)); + if (next == null) { + next = node.addNext(key.charAt(length - 1), value); + } + else { throw new IllegalStateException("Value '" + value + "' already in trie"); } } - public P get(final String key){ + /** + * Gets the value of a key. + * + * @param key the name of the key + * @return the value + */ + public P get(final String key) { Node

    node = root; final int length = key.length(); for (int i = 0; i < length; ++i) { node = node.getNext(key.charAt(i)); - if(node==null){ + if (node == null) { return null; } } @@ -65,25 +84,25 @@ private static final class Node

    { private final P value; private final CharMap> links = new CharMap>(); - public Node(final P value) { + Node(final P value) { this.value = value; } - public Node

    addNext(final char key){ + public Node

    addNext(final char key) { return addNext(key, null); } - public Node

    addNext(final char key, final P value){ - final Node

    next = new Node

    (value); + public Node

    addNext(final char key, final P currentValue) { + final Node

    next = new Node

    (currentValue); links.put(key, next); return next; } - public P getValue(){ + public P getValue() { return value; } - public Node

    getNext(final char key){ + public Node

    getNext(final char key) { return links.get(key); } } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/tries/WildcardTrie.java b/metafacture-commons/src/main/java/org/metafacture/commons/tries/WildcardTrie.java index 14f13ba8b..7efb6edac 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/tries/WildcardTrie.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/tries/WildcardTrie.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; import java.util.ArrayList; @@ -31,6 +32,7 @@ * @author Pascal Christoph */ public final class WildcardTrie

    { + public static final char STAR_WILDCARD = '*'; public static final char Q_WILDCARD = '?'; public static final String OR_STRING = "|"; @@ -42,25 +44,31 @@ public final class WildcardTrie

    { private Set> nextNodes = new HashSet>(); /** - * Inserts keys into the try. Use '|' to concatenate. Use '*' (0,inf) and - * '?' (1,1) to express wildcards. + * Creates an instance of {@link WildcardTrie}. + */ + public WildcardTrie() { + } + + /** + * Inserts keys into the trie. Use '|' to concatenate. Use '*' (0,inf) and '?' + * (1,1) to express wildcards. * - * @param keys pattern of keys to register - * @param value value to associate with the key pattern. + * @param keys pattern of keys to register + * @param value value to associate with the key pattern */ public void put(final String keys, final P value) { if (keys.contains(OR_STRING)) { final String[] keysSplit = OR_PATTERN.split(keys); - for (String string : keysSplit) { + for (final String string : keysSplit) { simplyPut(string, value); } - } else { + } + else { simplyPut(keys, value); } } private void simplyPut(final String key, final P value) { - final int length = key.length(); Node

    node = root; @@ -75,14 +83,18 @@ private void simplyPut(final String key, final P value) { node.addValue(value); } + /** + * Gets the List of values identified by a key. + * + * @param key the key + * @return the List of + */ public List

    get(final String key) { - nodes.add(root); final int length = key.length(); for (int i = 0; i < length; ++i) { - for (Node

    node : nodes) { - Node

    temp; - temp = node.getNext(key.charAt(i)); + for (final Node

    node : nodes) { + Node

    temp = node.getNext(key.charAt(i)); if (temp != null) { nextNodes.add(temp); } @@ -108,8 +120,12 @@ public List

    get(final String key) { nextNodes = temp; } + return matches(); + } + + private List

    matches() { List

    matches = Collections.emptyList(); - for (Node

    node : nodes) { + for (final Node

    node : nodes) { final Set

    values = node.getValues(); if (!values.isEmpty()) { if (matches == Collections.emptyList()) { @@ -161,4 +177,5 @@ public Node getNext(final char key) { return links.get(key); } } + } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/types/ListMap.java b/metafacture-commons/src/main/java/org/metafacture/commons/types/ListMap.java index 186bee82a..9f464980f 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/types/ListMap.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/types/ListMap.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.types; import java.util.ArrayList; @@ -28,23 +29,27 @@ * * @author Markus Michael Geipel * - * @param - * key - * @param - * value + * @param key + * @param value */ public class ListMap implements Map> { private String identifier; private final Map> map; + /** + * Creates an empty instance of an empty ListMap. + */ public ListMap() { - super(); map = new HashMap>(); } + /** + * Creates an instance of a ListMap with the given Map. + * + * @param map the Map + */ public ListMap(final Map> map) { - super(); this.map = map; } @@ -58,11 +63,21 @@ public final void clear() { identifier = null; } + /** + * Removes a key from the map. + * + * @param key the key + * @return the List of values or null if the map didn't contain the key + */ public final List removeKey(final K key) { return map.remove(key); } - + /** + * Clears all values of a key in the map. + * + * @param key the key of the map + */ public final void clearKey(final K key) { final List values = map.get(key); if (values != null) { @@ -70,8 +85,11 @@ public final void clearKey(final K key) { } } + /** + * Clears all values of all keys in the map. + */ public final void clearAllKeys() { - for (Entry> entry: map.entrySet()) { + for (final Entry> entry: map.entrySet()) { entry.getValue().clear(); } } @@ -86,30 +104,21 @@ public final Set keySet() { return map.keySet(); } - + /** + * Adds a value to the List of values of the key in the map. + * + * @param name the key + * @param value the value + */ public final void add(final K name, final V value) { - List values = map.get(name); if (values == null) { values = new ArrayList(); map.put(name, values); } - values.add(value); } - //@Override - public final void putAll(final K name, final Collection addValues) { - - List values = map.get(name); - if (values == null) { - values = new ArrayList(); - map.put(name, values); - } - - values.addAll(addValues); - } - @Override public final List get(final Object name) { final List values = map.get(name); @@ -119,10 +128,22 @@ public final List get(final Object name) { return values; } + /** + * Checks is a key exists in ListMap. + * + * @param name the name of the key + * @return true if the key exists, otherwise false + */ public final boolean existsKey(final K name) { return getFirst(name) != null; } + /** + * Gets the first element of the List of values of the key in the map or null. + * + * @param name the key + * @return first element of the values of the key or null + */ public final V getFirst(final K name) { final List values = map.get(name); if (values == null || values.isEmpty()) { @@ -136,10 +157,20 @@ public final String toString() { return map.toString(); } - public final void setId(final String identifier) { - this.identifier = identifier; + /** + * Sets the ID of the ListMap. + * + * @param newIdentifier the ID of the ListMap + */ + public final void setId(final String newIdentifier) { + identifier = newIdentifier; } + /** + * Gets the ID of the ListMap. + * + * @return the ID of the ListMap + */ public final String getId() { return identifier; } @@ -174,14 +205,30 @@ public final List remove(final Object key) { return map.remove(key); } + /** + * Adds a List of values to a key in the map. + * + * @param name the key + * @param addValues the List of values + */ + public final void putAll(final K name, final Collection addValues) { + List values = map.get(name); + if (values == null) { + values = new ArrayList(); + map.put(name, values); + } + + values.addAll(addValues); + } + @Override public final void putAll(final Map> putMap) { map.putAll(putMap); - } @Override public final Collection> values() { return map.values(); } + } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/types/NamedValue.java b/metafacture-commons/src/main/java/org/metafacture/commons/types/NamedValue.java index 46efa0221..f308e85c0 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/types/NamedValue.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/types/NamedValue.java @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.types; /** - * Stores an immutable name-value-pair. The hash code is + * Stores an immutable name-value pair. The hash code is * precomputed during instantiation. * * @author Markus Michael Geipel @@ -29,6 +30,12 @@ public final class NamedValue implements Comparable { private final String value; private final int preCompHashCode; + /** + * Constructs an immutable name-value pair by computing a hash code. + * + * @param name the name of the pair + * @param value the value of the pair + */ public NamedValue(final String name, final String value) { this.name = name; this.value = value; @@ -61,9 +68,9 @@ public int hashCode() { public boolean equals(final Object obj) { if (obj instanceof NamedValue) { final NamedValue namedValue = (NamedValue) obj; - return namedValue.preCompHashCode == preCompHashCode - && namedValue.name.equals(name) - && namedValue.value.equals(value); + return namedValue.preCompHashCode == preCompHashCode && + namedValue.name.equals(name) && + namedValue.value.equals(value); } return false; } diff --git a/metafacture-commons/src/main/java/org/metafacture/commons/types/ScopedHashMap.java b/metafacture-commons/src/main/java/org/metafacture/commons/types/ScopedHashMap.java index 0a00c4eaf..d22d2c603 100644 --- a/metafacture-commons/src/main/java/org/metafacture/commons/types/ScopedHashMap.java +++ b/metafacture-commons/src/main/java/org/metafacture/commons/types/ScopedHashMap.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.types; import java.util.HashMap; @@ -26,18 +27,24 @@ * @param type of the values * @author Markus Michael Geipel */ -public final class ScopedHashMap extends HashMap { +public final class ScopedHashMap extends HashMap { // checkstyle-disable-line IllegalType private static final long serialVersionUID = -7184066609960144713L; private final ScopedHashMap outerScope; + /** + * Creates an instance of {@link ScopedHashMap} and sets the outer scope. + * + * @param outerScope outer scope + */ public ScopedHashMap(final ScopedHashMap outerScope) { - super(); this.outerScope = outerScope; } + /** + * Creates an empty instance of {@link ScopedHashMap}. + */ public ScopedHashMap() { - super(); outerScope = null; } @@ -66,6 +73,11 @@ public V get(final Object key) { return ret; } + /** + * Gets the outer scope. + * + * @return the outer scope + */ public ScopedHashMap getOuterScope() { return outerScope; } diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/RequireTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/RequireTest.java index 6edbb437d..b08592b08 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/RequireTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/RequireTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons; import static org.junit.Assert.assertEquals; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/ResourceUtilTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/ResourceUtilTest.java index ddb48fa52..11ba1173a 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/ResourceUtilTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/ResourceUtilTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons; import static java.util.stream.Collectors.joining; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/StringUtilTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/StringUtilTest.java index 2b608c33f..3b527e0a6 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/StringUtilTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/StringUtilTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons; import static org.junit.Assert.assertEquals; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/TimeUtilTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/TimeUtilTest.java index cbfa8c910..ef5cb7561 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/TimeUtilTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/TimeUtilTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons; import static org.junit.Assert.assertEquals; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/XmlUtilTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/XmlUtilTest.java index e9056d260..c05ba03a3 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/XmlUtilTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/XmlUtilTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons; import static org.junit.Assert.assertEquals; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/tries/CharMapTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/tries/CharMapTest.java index c318d5728..79a14a586 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/tries/CharMapTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/tries/CharMapTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; import static org.junit.Assert.assertEquals; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/tries/SetMatchTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/tries/SetMatchTest.java index 4701323ba..a82461d5a 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/tries/SetMatchTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/tries/SetMatchTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; import static org.junit.Assert.assertEquals; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/tries/SetReplaceTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/tries/SetReplaceTest.java index 31838bb60..1376ea9f1 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/tries/SetReplaceTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/tries/SetReplaceTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; import static org.junit.Assert.assertEquals; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/tries/SimpleRegexTrieTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/tries/SimpleRegexTrieTest.java index 65e771ca4..46312a1b9 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/tries/SimpleRegexTrieTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/tries/SimpleRegexTrieTest.java @@ -12,6 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; import static org.junit.Assert.assertTrue; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/tries/SimpleTrieTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/tries/SimpleTrieTest.java index bf05c62a1..27263ed6b 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/tries/SimpleTrieTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/tries/SimpleTrieTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; import static org.junit.Assert.assertEquals; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/tries/WildcardTrieTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/tries/WildcardTrieTest.java index cf7bb3b52..5ad08b05f 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/tries/WildcardTrieTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/tries/WildcardTrieTest.java @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.tries; import static org.junit.Assert.assertEquals; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/types/ListMapTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/types/ListMapTest.java index 848e1d706..cb8fd793c 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/types/ListMapTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/types/ListMapTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.types; import static org.junit.Assert.assertEquals; diff --git a/metafacture-commons/src/test/java/org/metafacture/commons/types/NamedValueTest.java b/metafacture-commons/src/test/java/org/metafacture/commons/types/NamedValueTest.java index 5e2a41eff..fd405b747 100644 --- a/metafacture-commons/src/test/java/org/metafacture/commons/types/NamedValueTest.java +++ b/metafacture-commons/src/test/java/org/metafacture/commons/types/NamedValueTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.commons.types; import org.junit.Assert; diff --git a/metafacture-csv/src/main/java/org/metafacture/csv/CsvDecoder.java b/metafacture-csv/src/main/java/org/metafacture/csv/CsvDecoder.java index 3a39dc1a6..06bd6a690 100644 --- a/metafacture-csv/src/main/java/org/metafacture/csv/CsvDecoder.java +++ b/metafacture-csv/src/main/java/org/metafacture/csv/CsvDecoder.java @@ -13,11 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.csv; -import java.io.IOException; -import java.io.StringReader; -import java.util.List; +package org.metafacture.csv; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.StreamReceiver; @@ -28,67 +25,78 @@ import com.opencsv.CSVReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + /** - * Decodes lines of CSV files. First line is interpreted as header. + * Decodes lines of CSV files. First line may be interpreted as header. * * @author Markus Michael Geipel * @author Fabian Steeg (fsteeg) * */ -@Description("Decodes lines of CSV files. First line is interpreted as header.") +@Description("Decodes lines of CSV files. First line may be interpreted as header.") @In(String.class) @Out(StreamReceiver.class) @FluxCommand("decode-csv") public final class CsvDecoder extends DefaultObjectPipe { - private static final char DEFAULT_SEP = ','; - private char separator; + public static final char DEFAULT_SEP = ','; + private char separator = DEFAULT_SEP; private String[] header = new String[0]; private int count; private boolean hasHeader; /** + * Creates an instance of {@link CsvDecoder} with a given separator. + * * @param separator to split lines */ public CsvDecoder(final String separator) { - super(); this.separator = separator.charAt(0); } /** + * Creates an instance of {@link CsvDecoder} with a given separator. + * * @param separator to split lines */ public CsvDecoder(final char separator) { - super(); this.separator = separator; } + /** + * Creates an instance of {@link CsvDecoder}. The default separator is + * {@value #DEFAULT_SEP}. + */ public CsvDecoder() { - super(); - this.separator = DEFAULT_SEP; } @Override public void process(final String string) { assert !isClosed(); final String[] parts = parseCsv(string); - if(hasHeader){ - if(header.length==0){ + if (hasHeader) { + if (header.length == 0) { header = parts; - }else if(parts.length==header.length){ + } + else if (parts.length == header.length) { getReceiver().startRecord(String.valueOf(++count)); for (int i = 0; i < parts.length; ++i) { getReceiver().literal(header[i], parts[i]); } getReceiver().endRecord(); - }else{ + } + else { throw new IllegalArgumentException( String.format( "wrong number of columns (expected %s, was %s) in input line: %s", header.length, parts.length, string)); } - }else{ + } + else { getReceiver().startRecord(String.valueOf(++count)); for (int i = 0; i < parts.length; ++i) { getReceiver().literal(String.valueOf(i), parts[i]); @@ -107,15 +115,29 @@ private String[] parseCsv(final String string) { parts = lines.get(0); } reader.close(); - } catch (IOException e) { + } + catch (final IOException e) { e.printStackTrace(); } return parts; } + /** + * Flags if the CSV has a header or comes without a header. + * + * @param hasHeader true if the CSV has a header, otherwise false + */ public void setHasHeader(final boolean hasHeader) { this.hasHeader = hasHeader; } - public void setSeparator(final String separator) { this.separator = separator.charAt(0); } + /** + * Sets the separator. + * + * @param separator the separator as a String. The first character is used as + * the separator. + */ + public void setSeparator(final String separator) { + this.separator = separator.charAt(0); + } } diff --git a/metafacture-csv/src/test/java/org/metafacture/csv/CsvDecoderTest.java b/metafacture-csv/src/test/java/org/metafacture/csv/CsvDecoderTest.java index a7c345ca7..ed095383c 100644 --- a/metafacture-csv/src/test/java/org/metafacture/csv/CsvDecoderTest.java +++ b/metafacture-csv/src/test/java/org/metafacture/csv/CsvDecoderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.csv; import static org.mockito.Mockito.inOrder; diff --git a/metafacture-elasticsearch/build.gradle b/metafacture-elasticsearch/build.gradle index 8f942de23..64b80bc44 100644 --- a/metafacture-elasticsearch/build.gradle +++ b/metafacture-elasticsearch/build.gradle @@ -19,7 +19,7 @@ description = 'Modules for sending data to an Elasticsearch instance' dependencies { api project(':metafacture-framework') - implementation 'com.fasterxml.jackson.core:jackson-databind:2.8.5' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:2.5.5' } diff --git a/metafacture-elasticsearch/src/main/java/org/metafacture/elasticsearch/JsonToElasticsearchBulk.java b/metafacture-elasticsearch/src/main/java/org/metafacture/elasticsearch/JsonToElasticsearchBulk.java index 35155b44d..3dca1f339 100644 --- a/metafacture-elasticsearch/src/main/java/org/metafacture/elasticsearch/JsonToElasticsearchBulk.java +++ b/metafacture-elasticsearch/src/main/java/org/metafacture/elasticsearch/JsonToElasticsearchBulk.java @@ -13,8 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.elasticsearch; +import org.metafacture.framework.FluxCommand; +import org.metafacture.framework.ObjectReceiver; +import org.metafacture.framework.annotations.In; +import org.metafacture.framework.annotations.Out; +import org.metafacture.framework.helpers.DefaultObjectPipe; + +import com.fasterxml.jackson.databind.ObjectMapper; + import java.io.IOException; import java.io.StringWriter; import java.util.Arrays; @@ -24,14 +33,6 @@ import java.util.Set; import java.util.regex.Pattern; -import org.metafacture.framework.FluxCommand; -import org.metafacture.framework.ObjectReceiver; -import org.metafacture.framework.annotations.In; -import org.metafacture.framework.annotations.Out; -import org.metafacture.framework.helpers.DefaultObjectPipe; - -import com.fasterxml.jackson.databind.ObjectMapper; - /** * Add Elasticsearch bulk indexing metadata to JSON input. * @@ -42,133 +43,170 @@ @In(String.class) @Out(String.class) @FluxCommand("json-to-elasticsearch-bulk") -public class JsonToElasticsearchBulk extends - DefaultObjectPipe> { - - /** - * Use a MultiMap with Jackson to collect values from multiple fields with - * identical names under a single key. - */ - static class MultiMap extends HashMap { - private static final long serialVersionUID = 490682490432334605L; - - MultiMap() { - // default constructor for Jackson - } - - @Override - public Object put(String key, Object value) { - if (containsKey(key)) { - Object oldValue = get(key); - if (oldValue instanceof Set) { - @SuppressWarnings("unchecked") - Set vals = ((Set) oldValue); - vals.add(value); - return super.put(key, vals); - } - HashSet set = new HashSet<>(Arrays.asList(oldValue, value)); - return super.put(key, set.size() == 1 ? value : set); - } - return super.put(key, value); - } - } +public class JsonToElasticsearchBulk extends DefaultObjectPipe> { private ObjectMapper mapper = new ObjectMapper(); - private String[] idPath; + private String[] idPath = new String[] {}; private String type; private String index; - public void setIdKey(String idKey) { - this.idPath = new String[]{idKey}; - } - - public void setType(String type) { - this.type = type; - } - - public void setIndex(String index) { - this.index = index; - } - + /** + * Creates an instance of {@link JsonToElasticsearchBulk}. + */ public JsonToElasticsearchBulk() { - super(); - this.idPath = new String[]{}; - this.type = null; - this.index = null; } /** - * As an id is not required it can be omitted. + * Creates an instance of {@link JsonToElasticsearchBulk}. As an id is not + * required it can be omitted. * - * @param type The Elasticsearch index type + * @param type The Elasticsearch index type * @param index The Elasticsearch index name */ - public JsonToElasticsearchBulk(String type, String index) { - this(new String[] { }, type, index); + public JsonToElasticsearchBulk(final String type, final String index) { + this(new String[] {}, type, index); } /** - * @param idPath The key path of the JSON value to be used as the ID for the record - * @param type The Elasticsearch index type - * @param index The Elasticsearch index name + * Creates an instance of {@link JsonToElasticsearchBulk}. + * + * @param idPath the key path of the JSON value to be used as the ID for the + * record + * @param type the Elasticsearch index type + * @param index the Elasticsearch index name */ - public JsonToElasticsearchBulk(String[] idPath, String type, String index) { + public JsonToElasticsearchBulk(final String[] idPath, final String type, final String index) { this.idPath = idPath; this.type = type; this.index = index; } /** - * @param idKey The key of the JSON value to be used as the ID for the record - * @param type The Elasticsearch index type - * @param index The Elasticsearch index name + * Creates an instance of {@link JsonToElasticsearchBulk}. + * + * @param idKey the key of the JSON value to be used as the ID for the record + * @param type the Elasticsearch index type + * @param index the Elasticsearch index name */ - public JsonToElasticsearchBulk(String idKey, String type, String index) { + public JsonToElasticsearchBulk(final String idKey, final String type, final String index) { this(new String[]{idKey}, type, index); } /** - * @param idKey The key of the JSON value to be used as the ID for the record - * @param type The Elasticsearch index type - * @param index The Elasticsearch index name - * @param entitySeparator The separator between entity names in idKey + * + * Creates an instance of {@link JsonToElasticsearchBulk}. + * + * @param idKey the key of the JSON value to be used as the ID for the + * record + * @param type the Elasticsearch index type + * @param index the Elasticsearch index name + * @param entitySeparator the separator between entity names in idKey */ - public JsonToElasticsearchBulk(String idKey, String type, String index, String entitySeparator) { + public JsonToElasticsearchBulk(final String idKey, final String type, final String index, final String entitySeparator) { this(idKey.split(Pattern.quote(entitySeparator)), type, index); } + /** + * Sets the key path of the JSON value to be used as the ID for the record. + * + * @param idKey the key path of the JSON value + */ + public void setIdKey(final String idKey) { + this.idPath = new String[]{idKey}; + } + + /** + * Sets the name of the type of the index. + * + * @param type the name of the type of the index. + */ + public void setType(final String type) { + this.type = type; + } + + /** + * Sets the name of the index. + * + * @param index the name of the index + */ + public void setIndex(final String index) { + this.index = index; + } + @Override - public void process(String obj) { - StringWriter stringWriter = new StringWriter(); + public void process(final String obj) { + final StringWriter stringWriter = new StringWriter(); try { - Map json = mapper.readValue(obj, MultiMap.class); - Map detailsMap = new HashMap(); - Map indexMap = new HashMap(); + final Map json = mapper.readValue(obj, MultiMap.class); + final Map detailsMap = new HashMap(); + final Map indexMap = new HashMap(); indexMap.put("index", detailsMap); - if (idPath.length > 0) detailsMap.put("_id", findId(json)); + if (idPath.length > 0) { + detailsMap.put("_id", findId(json)); + } detailsMap.put("_type", type); detailsMap.put("_index", index); mapper.writeValue(stringWriter, indexMap); stringWriter.write("\n"); mapper.writeValue(stringWriter, json); - } catch (IOException e) { + } + catch (final IOException e) { e.printStackTrace(); } getReceiver().process(stringWriter.toString()); } - private Object findId(Object value) { + private Object findId(final Object value) { + Object newValue = value; + for (final String key : idPath) { - if (value instanceof Map) { + if (newValue instanceof Map) { @SuppressWarnings("unchecked") - final Map nestedMap = (Map) value; - value = nestedMap.get(key); + final Map nestedMap = (Map) newValue; + newValue = nestedMap.get(key); } else { return null; } } - return value; + return newValue; } + + /** + * Use a MultiMap with Jackson to collect values from multiple fields with + * identical names under a single key. + */ + static class MultiMap extends HashMap { // checkstyle-disable-line IllegalType + private static final long serialVersionUID = 490682490432334605L; + + MultiMap() { + // default constructor for Jackson + } + + @Override + public Object put(final String key, final Object value) { + final Object newValue; + + if (containsKey(key)) { + final Object oldValue = get(key); + if (oldValue instanceof Set) { + @SuppressWarnings("unchecked") + final Set vals = (Set) oldValue; + vals.add(value); + newValue = vals; + } + else { + final Set set = new HashSet<>(Arrays.asList(oldValue, value)); + newValue = set.size() == 1 ? value : set; + } + } + else { + newValue = value; + } + + return super.put(key, newValue); + } + } + } diff --git a/metafacture-elasticsearch/src/test/java/org/metafacture/elasticsearch/JsonToElasticsearchBulkTest.java b/metafacture-elasticsearch/src/test/java/org/metafacture/elasticsearch/JsonToElasticsearchBulkTest.java index db20e36df..ae715271a 100644 --- a/metafacture-elasticsearch/src/test/java/org/metafacture/elasticsearch/JsonToElasticsearchBulkTest.java +++ b/metafacture-elasticsearch/src/test/java/org/metafacture/elasticsearch/JsonToElasticsearchBulkTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.elasticsearch; import static org.mockito.Mockito.verify; diff --git a/metafacture-files/src/main/java/org/metafacture/files/DirReader.java b/metafacture-files/src/main/java/org/metafacture/files/DirReader.java index c477cb285..c357ead03 100644 --- a/metafacture-files/src/main/java/org/metafacture/files/DirReader.java +++ b/metafacture-files/src/main/java/org/metafacture/files/DirReader.java @@ -13,11 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.files; -import java.io.File; -import java.io.FilenameFilter; -import java.util.Arrays; +package org.metafacture.files; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.ObjectReceiver; @@ -26,6 +23,10 @@ import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultObjectPipe; +import java.io.File; +import java.io.FilenameFilter; +import java.util.Arrays; + /** * Reads a directory and emits all filenames found. * @@ -40,14 +41,31 @@ public final class DirReader extends DefaultObjectPipe receiver = getReceiver(); - final File[] files = filenameFilterPattern == null ? dir.listFiles() - : dir.listFiles(new FilenameFilter() { + final File[] files = filenameFilterPattern == null ? dir.listFiles() : + dir.listFiles(new FilenameFilter() { @Override public boolean accept(final File dir, final String name) { return name.matches(filenameFilterPattern); } }); Arrays.sort(files); - for (File file : files) { + for (final File file : files) { if (file.isDirectory()) { if (recursive) { dir(file); } - } else { + } + else { receiver.process(file.getAbsolutePath()); } } diff --git a/metafacture-files/src/main/java/org/metafacture/files/FileDigestCalculator.java b/metafacture-files/src/main/java/org/metafacture/files/FileDigestCalculator.java index 7c4c99dac..f2e3d8823 100644 --- a/metafacture-files/src/main/java/org/metafacture/files/FileDigestCalculator.java +++ b/metafacture-files/src/main/java/org/metafacture/files/FileDigestCalculator.java @@ -13,13 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.files; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +package org.metafacture.files; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.MetafactureException; @@ -30,6 +25,12 @@ import org.metafacture.framework.helpers.DefaultObjectPipe; import org.metafacture.framework.objects.Triple; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + /** * Interprets the input string as a file name and computes a cryptographic hash * for the file. @@ -48,18 +49,28 @@ public final class FileDigestCalculator extends private static final int HIGH_NIBBLE = 0xf0; private static final int LOW_NIBBLE = 0x0f; - private static final char[] NIBBLE_TO_HEX = - { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + private static final char[] NIBBLE_TO_HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + private static final int NIBBLE_TO_HEX_SHIFT_WIDTH = 4; private final DigestAlgorithm algorithm; private final MessageDigest messageDigest; - + /** + * Uses the given DigestAlgorithm to define the MessageDigest. + * + * @param algorithm the DigestAlgorithm + */ public FileDigestCalculator(final DigestAlgorithm algorithm) { this.algorithm = algorithm; this.messageDigest = this.algorithm.getInstance(); } + /** + * Uses the given name of the algorithm to define the MessageDigest. + * + * @param algorithm the name of the algorithm + */ public FileDigestCalculator(final String algorithm) { this.algorithm = DigestAlgorithm.valueOf(algorithm.toUpperCase()); this.messageDigest = this.algorithm.getInstance(); @@ -72,12 +83,17 @@ public void process(final String file) { try { stream = new FileInputStream(file); digest = bytesToHex(getDigest(stream, messageDigest)); - } catch (IOException e) { + } + catch (final IOException e) { throw new MetafactureException(e); - } finally { + } + finally { if (stream != null) { - try { stream.close(); } - catch (final IOException e) { } + try { + stream.close(); + } + catch (final IOException e) { + } } } getReceiver().process(new Triple(file, algorithm.name(), digest)); @@ -96,8 +112,8 @@ private static byte[] getDigest(final InputStream stream, final MessageDigest me private static String bytesToHex(final byte[] bytes) { final char[] hex = new char[bytes.length * 2]; - for (int i=0; i < bytes.length; ++i) { - hex[i * 2] = NIBBLE_TO_HEX[(bytes[i] & HIGH_NIBBLE) >>> 4]; + for (int i = 0; i < bytes.length; ++i) { + hex[i * 2] = NIBBLE_TO_HEX[(bytes[i] & HIGH_NIBBLE) >>> NIBBLE_TO_HEX_SHIFT_WIDTH]; hex[i * 2 + 1] = NIBBLE_TO_HEX[bytes[i] & LOW_NIBBLE]; } return new String(hex); @@ -115,19 +131,26 @@ public enum DigestAlgorithm { SHA1("SHA-1"), SHA256("SHA-256"), SHA384("SHA-384"), - SHA512 ("SHA-512"); + SHA512("SHA-512"); private final String identifier; - private DigestAlgorithm(final String identifier) { + DigestAlgorithm(final String identifier) { this.identifier = identifier; } + /** + * Returns a MessageDigest object that implements the specified digest + * algorithm. + * + * @return the MessageDigest + */ public MessageDigest getInstance() { try { return MessageDigest.getInstance(identifier); - } catch (NoSuchAlgorithmException e) { - throw new MetafactureException (e); + } + catch (final NoSuchAlgorithmException e) { + throw new MetafactureException(e); } } diff --git a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/CloseSuppressor.java b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/CloseSuppressor.java index b4cd8417b..cb9fb08f5 100644 --- a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/CloseSuppressor.java +++ b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/CloseSuppressor.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flowcontrol; import org.metafacture.framework.FluxCommand; @@ -43,10 +44,23 @@ public final class CloseSuppressor private final int numCloses; private int count; + /** + * Creates an instance of {@link CloseSuppressor} with a number defining the + * number of events to block before closing the stream. + * + * @param numCloses the number of events to block before closing the stream. The + * String is parsed to an integer. + */ public CloseSuppressor(final String numCloses) { this(Integer.parseInt(numCloses)); } + /** + * Creates an instance of {@link CloseSuppressor} with a number defining the + * number of events to block before closing the stream. + * + * @param numCloses the number of events to block before closing the stream + */ public CloseSuppressor(final int numCloses) { this.numCloses = numCloses; } @@ -59,9 +73,9 @@ public void process(final T obj) { } @Override - public > R setReceiver(final R receiver) { - this.receiver = receiver; - return receiver; + public > R setReceiver(final R newReceiver) { + receiver = newReceiver; + return newReceiver; } @Override diff --git a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectBatchResetter.java b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectBatchResetter.java index bff40c514..8a5efdd78 100644 --- a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectBatchResetter.java +++ b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectBatchResetter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flowcontrol; import org.metafacture.framework.FluxCommand; @@ -41,23 +42,33 @@ public class ObjectBatchResetter extends DefaultObjectPipereset-stream event is triggered. *

    - * The default value is {@value DEFAULT_BATCH_SIZE}. + * The default value is {@value #DEFAULT_BATCH_SIZE}. *

    - * This parameter can be changed anytime during processing. If the new value - * is less than the number of received objects a reset-stream event is + * This parameter can be changed anytime during processing. If the new value is + * less than the number of received objects a reset-stream event is * emitted when the next object is received. * * @param batchSize number of objects before a reset-stream event is * triggered */ - public void setBatchSize(int batchSize) { - + public void setBatchSize(final int batchSize) { this.batchSize = batchSize; } + /** + * Gets the size of the batch. + * + * @return the size of the batch + */ public int getBatchSize() { return batchSize; } diff --git a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectExceptionCatcher.java b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectExceptionCatcher.java index 934425f50..153f1ef17 100644 --- a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectExceptionCatcher.java +++ b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectExceptionCatcher.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.flowcontrol; -import java.io.PrintWriter; -import java.io.StringWriter; +package org.metafacture.flowcontrol; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.ObjectReceiver; @@ -24,9 +22,12 @@ import org.metafacture.framework.annotations.In; import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultObjectPipe; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.PrintWriter; +import java.io.StringWriter; /** * Wraps the call to the process method of the downstream module @@ -51,38 +52,76 @@ public final class ObjectExceptionCatcher extends private String logPrefix; private boolean logStackTrace; + private boolean logExceptionMessage = true; + /** + * Creates an instance of {@link ObjectExceptionCatcher} without a log message + * prefix. + */ public ObjectExceptionCatcher() { this(""); } + /** + * Creates an instance of {@link ObjectExceptionCatcher} with a given prefix of + * the log messages. + * + * @param logPrefix the prefix of the log messages + */ public ObjectExceptionCatcher(final String logPrefix) { - super(); this.logPrefix = logPrefix; } + /** + * Sets the log prefix. + * + * @param logPrefix the log message prefix + */ public void setLogPrefix(final String logPrefix) { this.logPrefix = logPrefix; } + /** + * Gets the log messages prefix. + * + * @return the log message prefix + */ public String getLogPrefix() { return logPrefix; } + /** + * Sets the log messages to stack trace level. + * + * @param logStackTrace true if the log messages should be set to stack trace + * level. + */ public void setLogStackTrace(final boolean logStackTrace) { this.logStackTrace = logStackTrace; } + /** + * Checks whether the log messages should be in stack trace level. + * + * @return true if the log messages are in stack trace level + */ public boolean isLogStackTrace() { return logStackTrace; } + /*package-private*/ void setLogExceptionMessage(final boolean logExceptionMessage) { + this.logExceptionMessage = logExceptionMessage; + } + @Override public void process(final T obj) { try { getReceiver().process(obj); - } catch(final Exception e) { - LOG.error("{}'{}' while processing object: {}", logPrefix, e.getMessage(), obj); + } + catch (final Exception e) { // checkstyle-disable-line IllegalCatch + if (logExceptionMessage) { + LOG.error("{}'{}' while processing object: {}", logPrefix, e.getMessage(), obj); + } if (logStackTrace) { final StringWriter stackTraceWriter = new StringWriter(); diff --git a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectPipeDecoupler.java b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectPipeDecoupler.java index a91981c06..b801d7097 100644 --- a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectPipeDecoupler.java +++ b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectPipeDecoupler.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.flowcontrol; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; +package org.metafacture.flowcontrol; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.ObjectPipe; @@ -24,9 +22,13 @@ import org.metafacture.framework.annotations.Description; import org.metafacture.framework.annotations.In; import org.metafacture.framework.annotations.Out; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + /** * Creates a new thread in which subsequent flow elements run. * @@ -49,18 +51,37 @@ public final class ObjectPipeDecoupler implements ObjectPipe receiver; private boolean debug; + /** + * Creates an instance of {@link ObjectPipeDecoupler} by setting a default + * capacity of {@value #DEFAULT_CAPACITY}. + */ public ObjectPipeDecoupler() { queue = new LinkedBlockingQueue<>(DEFAULT_CAPACITY); } + /** + * Creates an instance of {@link ObjectPipeDecoupler} by setting a capacity. + * + * @param capacity the capacity + */ public ObjectPipeDecoupler(final int capacity) { queue = new LinkedBlockingQueue<>(capacity); } + /** + * Creates an instance of {@link ObjectPipeDecoupler} by setting a capacity. + * + * @param capacity the capacity as String. Will be parsed as integer. + */ public ObjectPipeDecoupler(final String capacity) { queue = new LinkedBlockingQueue<>(Integer.parseInt(capacity)); } + /** + * Sets the log messages to be more verbose. + * + * @param debug true if the log messages should be more verbose + */ public void setDebug(final boolean debug) { this.debug = debug; } @@ -76,7 +97,8 @@ public void process(final T obj) { if (debug) { LOG.info("Current buffer size: {}", queue.size()); } - } catch (InterruptedException e) { + } + catch (final InterruptedException e) { Thread.currentThread().interrupt(); } } @@ -87,20 +109,21 @@ private void start() { } @Override - public > R setReceiver(final R receiver) { + public > R setReceiver(final R newReceiver) { if (null != thread) { throw new IllegalStateException("Receiver cannot be changed while processing thread is running."); } - this.receiver = receiver; - return receiver; + receiver = newReceiver; + return newReceiver; } @Override public void resetStream() { try { queue.put(Feeder.BLUE_PILL); - } catch (InterruptedException e) { + } + catch (final InterruptedException e) { Thread.currentThread().interrupt(); } } @@ -110,7 +133,8 @@ public void closeStream() { try { queue.put(Feeder.RED_PILL); thread.join(); - } catch (InterruptedException e) { + } + catch (final InterruptedException e) { Thread.currentThread().interrupt(); } thread = null; @@ -128,7 +152,7 @@ static final class Feeder implements Runnable { private final ObjectReceiver receiver; private final BlockingQueue queue; - public Feeder(final ObjectReceiver receiver, final BlockingQueue queue) { + Feeder(final ObjectReceiver receiver, final BlockingQueue queue) { this.receiver = receiver; this.queue = queue; } @@ -150,7 +174,8 @@ public void run() { } receiver.process((T) object); } - } catch (InterruptedException e) { + } + catch (final InterruptedException e) { Thread.currentThread().interrupt(); return; } diff --git a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectThreader.java b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectThreader.java index 903ce5aba..e4aa872d6 100644 --- a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectThreader.java +++ b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/ObjectThreader.java @@ -1,5 +1,5 @@ /* Copyright 2019 Pascal Christoph (hbz), and others. - * + * * 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 @@ -15,9 +15,6 @@ package org.metafacture.flowcontrol; -import java.util.ArrayList; -import java.util.List; - import org.metafacture.framework.FluxCommand; import org.metafacture.framework.ObjectPipe; import org.metafacture.framework.ObjectReceiver; @@ -25,20 +22,24 @@ import org.metafacture.framework.annotations.Description; import org.metafacture.framework.annotations.In; import org.metafacture.framework.annotations.Out; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; + /** * Divides incoming objects and distributes them to added receivers. These * receivers are coupled with an * {@link org.metafacture.flowcontrol.ObjectPipeDecoupler}, so each added * receiver runs in its own thread. - * + * * @param Object type * * @author Pascal Christoph (dr0i) * @author Fabian Steeg (fsteeg) - * + * */ @In(Object.class) @Out(Object.class) @@ -48,22 +49,29 @@ public class ObjectThreader implements Tee>, ObjectPipe> receivers = new ArrayList>(); - private int objectNumber = 0; + private int objectNumber; + + /** + * Creates an instance of {@link ObjectThreader}. + */ + public ObjectThreader() { + } @Override public void process(final T obj) { receivers.get(objectNumber).process(obj); if (objectNumber == receivers.size() - 1) { objectNumber = 0; - } else { - objectNumber++; + } + else { + ++objectNumber; } } @Override public Tee> addReceiver(final ObjectReceiver receiver) { - LOG.info("Adding thread {}", (receivers.size() + 1)); - ObjectPipeDecoupler opd = new ObjectPipeDecoupler<>(); + LOG.info("Adding thread {}", receivers.size() + 1); + final ObjectPipeDecoupler opd = new ObjectPipeDecoupler<>(); opd.setReceiver(receiver); receivers.add(opd); return this; @@ -77,7 +85,7 @@ public > R setReceiver(final R receiver) { } @Override - public > R setReceivers(R receiver, ObjectReceiver lateralReceiver) { + public > R setReceivers(final R receiver, final ObjectReceiver lateralReceiver) { receivers.clear(); addReceiver(receiver); addReceiver(lateralReceiver); @@ -95,7 +103,7 @@ public void closeStream() { } @Override - public Tee> removeReceiver(ObjectReceiver receiver) { + public Tee> removeReceiver(final ObjectReceiver receiver) { receivers.remove(receiver); return this; } diff --git a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamBatchResetter.java b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamBatchResetter.java index df505c79a..dcad1b38b 100644 --- a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamBatchResetter.java +++ b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamBatchResetter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flowcontrol; import org.metafacture.framework.FluxCommand; @@ -40,35 +41,61 @@ public final class StreamBatchResetter extends ForwardingStreamPipe { private long recordCount; private long batchCount; - public final void setBatchSize(final int batchSize) { + /** + * Creates an instance of {@link StreamBatchResetter}. + */ + public StreamBatchResetter() { + } + + /** + * Sets the size of the batch. + * + * @param batchSize the size of the batch + */ + public void setBatchSize(final int batchSize) { this.batchSize = batchSize; } - public final long getBatchSize() { + /** + * Gets the size of the batch. + * + * @return the size of the batch + */ + public long getBatchSize() { return batchSize; } - public final long getBatchCount() { + /** + * Gets the batch count. + * + * @return the number of counted batches. + */ + public long getBatchCount() { return batchCount; } - public final long getRecordCount() { + /** + * Gets the record count. + * + * @return the number of counted records + */ + public long getRecordCount() { return recordCount; } @Override - public final void endRecord() { + public void endRecord() { getReceiver().endRecord(); - recordCount++; + ++recordCount; recordCount %= batchSize; if (recordCount == 0) { - batchCount++; + ++batchCount; getReceiver().resetStream(); } } @Override - protected final void onResetStream() { + protected void onResetStream() { recordCount = 0; batchCount = 0; } diff --git a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamBuffer.java b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamBuffer.java index 0e8f985a6..fe2dea710 100644 --- a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamBuffer.java +++ b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamBuffer.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.flowcontrol; -import java.util.ArrayList; -import java.util.List; +package org.metafacture.flowcontrol; import org.metafacture.framework.StreamPipe; import org.metafacture.framework.StreamReceiver; import org.metafacture.framework.helpers.DefaultStreamPipe; +import java.util.ArrayList; +import java.util.List; /** * {@link StreamPipe} which buffers incoming records and replays them upon @@ -30,8 +30,7 @@ * @author Markus Michael Geipel * */ -public final class StreamBuffer - extends DefaultStreamPipe { +public final class StreamBuffer extends DefaultStreamPipe { /** * Defines entity and literal message types. @@ -43,8 +42,18 @@ private enum MessageType { private final List typeBuffer = new ArrayList(); private final List valueBuffer = new ArrayList(); + /** + * Creates an instance of {@link StreamBuffer}. + */ + public StreamBuffer() { + } - public boolean isEmpty(){ + /** + * Checks whether there are messages stored. + * + * @return true if messages are empty, otherwise false + */ + public boolean isEmpty() { return typeBuffer.isEmpty(); } @@ -54,31 +63,34 @@ public boolean isEmpty(){ public void replay() { int index = 0; - for (MessageType type : typeBuffer) { + for (final MessageType type : typeBuffer) { switch (type) { - case RECORD_START: - getReceiver().startRecord(valueBuffer.get(index)); - ++index; - break; - case RECORD_END: - getReceiver().endRecord(); - break; - - case ENTITY_START: - getReceiver().startEntity(valueBuffer.get(index)); - ++index; - break; - case ENTITY_END: - getReceiver().endEntity(); - break; - default: - getReceiver().literal(valueBuffer.get(index), valueBuffer.get(index+1)); - index +=2; - break; + case RECORD_START: + getReceiver().startRecord(valueBuffer.get(index)); + ++index; + break; + case RECORD_END: + getReceiver().endRecord(); + break; + + case ENTITY_START: + getReceiver().startEntity(valueBuffer.get(index)); + ++index; + break; + case ENTITY_END: + getReceiver().endEntity(); + break; + default: + getReceiver().literal(valueBuffer.get(index), valueBuffer.get(index + 1)); + index += 2; + break; } } } + /** + * Clears the buffer. + */ public void clear() { typeBuffer.clear(); valueBuffer.clear(); diff --git a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamDeferrer.java b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamDeferrer.java index 60a48c250..b03888670 100644 --- a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamDeferrer.java +++ b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamDeferrer.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flowcontrol; import org.metafacture.framework.FluxCommand; @@ -44,6 +45,12 @@ public class StreamDeferrer extends DefaultStreamPipe { private final StreamBuffer buffer = new StreamBuffer(); + /** + * Creates an instance of {@link StreamDeferrer}. + */ + public StreamDeferrer() { + } + @Override public void startRecord(final String identifier) { buffer.clear(); diff --git a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamExceptionCatcher.java b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamExceptionCatcher.java index e19f41a19..20c7b684d 100644 --- a/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamExceptionCatcher.java +++ b/metafacture-flowcontrol/src/main/java/org/metafacture/flowcontrol/StreamExceptionCatcher.java @@ -14,9 +14,6 @@ * limitations under the License. */ -/** - * - */ package org.metafacture.flowcontrol; import org.metafacture.framework.FluxCommand; @@ -25,6 +22,7 @@ import org.metafacture.framework.annotations.In; import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultStreamPipe; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,12 +44,20 @@ public final class StreamExceptionCatcher extends private final String logPrefix; + /** + * Creates an instance of {@link StreamExceptionCatcher}. + */ public StreamExceptionCatcher() { this(""); } + /** + * Creates an instance of {@link StreamExceptionCatcher} setting a prefix for + * the logs messages. + * + * @param logPrefix the prefix of the log messages + */ public StreamExceptionCatcher(final String logPrefix) { - super(); this.logPrefix = logPrefix; } @@ -59,7 +65,8 @@ public StreamExceptionCatcher(final String logPrefix) { public void startRecord(final String identifier) { try { getReceiver().startRecord(identifier); - } catch(final Exception e) { + } + catch (final Exception e) { // checkstyle-disable-line IllegalCatch LOG.error(MSG_PATTERN, logPrefix, "StartRecord" + identifier); LOG.error(MSG_PATTERN, logPrefix, e); } @@ -69,7 +76,8 @@ public void startRecord(final String identifier) { public void endRecord() { try { getReceiver().endRecord(); - } catch(final Exception e) { + } + catch (final Exception e) { // checkstyle-disable-line IllegalCatch LOG.error(MSG_PATTERN, logPrefix, "endRecord"); LOG.error(MSG_PATTERN, logPrefix, e); } @@ -79,7 +87,8 @@ public void endRecord() { public void startEntity(final String name) { try { getReceiver().startEntity(name); - } catch(final Exception e) { + } + catch (final Exception e) { // checkstyle-disable-line IllegalCatch LOG.error(MSG_PATTERN, logPrefix, "startEntity" + name); LOG.error(MSG_PATTERN, logPrefix, e); } @@ -89,7 +98,8 @@ public void startEntity(final String name) { public void endEntity() { try { getReceiver().endEntity(); - } catch(final Exception e) { + } + catch (final Exception e) { // checkstyle-disable-line IllegalCatch LOG.error(MSG_PATTERN, logPrefix, "endEntity"); LOG.error(MSG_PATTERN, logPrefix, e); } @@ -99,8 +109,9 @@ public void endEntity() { public void literal(final String name, final String value) { try { getReceiver().literal(name, value); - } catch(final Exception e) { - LOG.error(MSG_PATTERN, logPrefix, "literal " + name +" " + value); + } + catch (final Exception e) { // checkstyle-disable-line IllegalCatch + LOG.error(MSG_PATTERN, logPrefix, "literal " + name + " " + value); LOG.error(MSG_PATTERN, logPrefix, e); } } diff --git a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/CloseSuppressorTest.java b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/CloseSuppressorTest.java index dd7681cdc..f28c5b334 100644 --- a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/CloseSuppressorTest.java +++ b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/CloseSuppressorTest.java @@ -13,11 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flowcontrol; +import org.junit.Rule; import org.junit.Test; import org.metafacture.framework.ObjectReceiver; +import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; /** * Tests for class {@link CloseSuppressor}. @@ -27,10 +32,15 @@ */ public final class CloseSuppressorTest { + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private ObjectReceiver receiver; + @Test public void testSuppression() { final CloseSuppressor supressor = new CloseSuppressor<>(3); - final ObjectReceiver receiver = Mockito.mock(ObjectReceiver.class); supressor.setReceiver(receiver); supressor.closeStream(); supressor.closeStream(); diff --git a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectBatchResetterTest.java b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectBatchResetterTest.java index f857466c3..e28ae2a52 100644 --- a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectBatchResetterTest.java +++ b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectBatchResetterTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flowcontrol; import static org.assertj.core.api.Assertions.assertThat; diff --git a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectExceptionCatcherTest.java b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectExceptionCatcherTest.java index 0e27b3ba3..3dd3681a8 100644 --- a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectExceptionCatcherTest.java +++ b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectExceptionCatcherTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flowcontrol; import static org.mockito.ArgumentMatchers.anyString; @@ -44,6 +45,7 @@ public void setup() { .when(exceptionThrowingModule).process(anyString()); exceptionCatcher = new ObjectExceptionCatcher<>(); exceptionCatcher.setReceiver(exceptionThrowingModule); + exceptionCatcher.setLogExceptionMessage(false); } @Test diff --git a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectThreaderTest.java b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectThreaderTest.java index 240d37a43..ae88b18a1 100644 --- a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectThreaderTest.java +++ b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/ObjectThreaderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flowcontrol; import static org.assertj.core.api.Assertions.assertThat; diff --git a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/StreamBufferTest.java b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/StreamBufferTest.java index d5db16737..4473c99f1 100644 --- a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/StreamBufferTest.java +++ b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/StreamBufferTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flowcontrol; import static org.mockito.Mockito.inOrder; diff --git a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/StreamDeferrerTest.java b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/StreamDeferrerTest.java index 0f7a2d582..ee5e27045 100644 --- a/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/StreamDeferrerTest.java +++ b/metafacture-flowcontrol/src/test/java/org/metafacture/flowcontrol/StreamDeferrerTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flowcontrol; import static org.mockito.Mockito.inOrder; diff --git a/metafacture-flux/build.gradle b/metafacture-flux/build.gradle index 6cf52352b..14dda1402 100644 --- a/metafacture-flux/build.gradle +++ b/metafacture-flux/build.gradle @@ -23,6 +23,7 @@ dependencies { api project(':metafacture-framework') implementation project(':metafacture-commons') implementation project(':metafacture-io') + testRuntime project(':metafacture-plumbing') antlr 'org.antlr:antlr:3.5.2' testImplementation 'junit:junit:4.12' } diff --git a/metafacture-flux/src/main/antlr/org/metafacture/flux/parser/Flux.g b/metafacture-flux/src/main/antlr/org/metafacture/flux/parser/Flux.g index 7f2cb3d76..643a70090 100644 --- a/metafacture-flux/src/main/antlr/org/metafacture/flux/parser/Flux.g +++ b/metafacture-flux/src/main/antlr/org/metafacture/flux/parser/Flux.g @@ -33,16 +33,53 @@ tokens { @header { package org.metafacture.flux.parser; + +import org.metafacture.flux.FluxParseException; } @lexer::header { package org.metafacture.flux.parser; } +@parser::members { + // ensure throwing an exception + @Override + protected Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) throws RecognitionException { + { + RecognitionException e = new MismatchedTokenException(ttype, input); + // if next token is what we are looking for then "delete" this token + if ( mismatchIsUnwantedToken(input, ttype) ) { + e = new UnwantedTokenException(ttype, input); + beginResync(); + input.consume(); // simply delete extra token + endResync(); + reportError(e); // report after consuming so AW sees the token in the exception + // we want to return the token we're actually matching + Object matchedSymbol = getCurrentInputSymbol(input); + input.consume(); // move past ttype token as if all were ok + } + // can't recover with single token deletion, try insertion + if ( mismatchIsMissingToken(input, follow) ) { + Object inserted = getMissingSymbol(input, e, ttype, follow); + e = new MissingTokenException(ttype, input, inserted); + reportError(e); // report after inserting so AW sees the token in the exception + } + throw e; + } + } +} + flux : varDef* flow* ; +catch [RecognitionException re] { + reportError(re); + recover(input,re); + retval.tree = (CommonTree)adaptor.errorNode(input, retval.start, input.LT(-1), re); + String msg = getErrorMessage(re, this.getTokenNames()) + " in Flux"; + throw new FluxParseException(msg, re); +} varDef : @@ -53,6 +90,9 @@ varDef -> ^(ASSIGN Identifier exp) ; +catch [RecognitionException re] { + throw re; +} flow : @@ -63,6 +103,9 @@ flow ) '|'! flowtail ('|'! Wormhole)? ';'! ; +catch [RecognitionException re] { + throw re; +} tee : @@ -73,6 +116,9 @@ tee ^(SUBFLOW flowtail)+ ) ; +catch [RecognitionException re] { + throw re; +} flowtail : @@ -88,6 +134,9 @@ flowtail ) )* ; +catch [RecognitionException re] { + throw re; +} StdIn : @@ -105,6 +154,9 @@ exp : atom ('+'^ atom)* ; +catch [RecognitionException re] { + throw re; +} atom : @@ -122,6 +174,9 @@ pipeArgs ) (','! namedArg)* ; +catch [RecognitionException re] { + throw re; +} namedArg : diff --git a/metafacture-flux/src/main/java/org/metafacture/flux/FluxCompiler.java b/metafacture-flux/src/main/java/org/metafacture/flux/FluxCompiler.java index febc7a630..2dd718b78 100644 --- a/metafacture-flux/src/main/java/org/metafacture/flux/FluxCompiler.java +++ b/metafacture-flux/src/main/java/org/metafacture/flux/FluxCompiler.java @@ -13,20 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flux; -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; +import org.metafacture.flux.parser.FlowBuilder; +import org.metafacture.flux.parser.FluxLexer; +import org.metafacture.flux.parser.FluxParser; +import org.metafacture.flux.parser.FluxProgramm; import org.antlr.runtime.ANTLRInputStream; import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.RecognitionException; import org.antlr.runtime.tree.CommonTreeNodeStream; -import org.metafacture.flux.parser.FlowBuilder; -import org.metafacture.flux.parser.FluxLexer; -import org.metafacture.flux.parser.FluxParser; -import org.metafacture.flux.parser.FluxProgramm; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; /** * Creates a flow based on a flux script. @@ -38,7 +40,17 @@ private FluxCompiler() { // no instances } - public static FluxProgramm compile(final InputStream flux, final Map vars ) throws RecognitionException, IOException{ + /** + * Compiles the flux to a flow. + * + * @see FluxProgramm + * @param flux the flux + * @param vars the variables of the flux + * @return the flow + * @throws RecognitionException if an ANTLR exception occurs + * @throws IOException if an I/O error occurs + */ + public static FluxProgramm compile(final InputStream flux, final Map vars) throws RecognitionException, IOException { return compileFlow(compileAst(flux), vars); } diff --git a/metafacture-flux/src/main/java/org/metafacture/flux/FluxParseException.java b/metafacture-flux/src/main/java/org/metafacture/flux/FluxParseException.java index 2811b6d77..576d31711 100644 --- a/metafacture-flux/src/main/java/org/metafacture/flux/FluxParseException.java +++ b/metafacture-flux/src/main/java/org/metafacture/flux/FluxParseException.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flux; import org.metafacture.framework.MetafactureException; @@ -25,10 +26,22 @@ public final class FluxParseException extends MetafactureException { private static final long serialVersionUID = -5728526458760884738L; + /** + * Creates an instance of {@link FluxParseException} by a given message and the + * cause of the exception. + * + * @param message the message + * @param cause the cause + */ public FluxParseException(final String message, final Throwable cause) { super(message, cause); } + /** + * Creates an instance of {@link FluxParseException} by a given message. + * + * @param message the message + */ public FluxParseException(final String message) { super(message); } diff --git a/metafacture-flux/src/main/java/org/metafacture/flux/HelpPrinter.java b/metafacture-flux/src/main/java/org/metafacture/flux/HelpPrinter.java index fbe2410ee..045a81c42 100644 --- a/metafacture-flux/src/main/java/org/metafacture/flux/HelpPrinter.java +++ b/metafacture-flux/src/main/java/org/metafacture/flux/HelpPrinter.java @@ -13,8 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flux; +import org.metafacture.commons.ResourceUtil; +import org.metafacture.commons.reflection.ConfigurableClass; +import org.metafacture.commons.reflection.ObjectFactory; +import org.metafacture.flux.parser.FluxProgramm; +import org.metafacture.framework.MetafactureException; +import org.metafacture.framework.annotations.Description; +import org.metafacture.framework.annotations.In; +import org.metafacture.framework.annotations.Out; +import org.metafacture.framework.annotations.ReturnsAvailableArguments; + import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; @@ -27,24 +38,35 @@ import java.util.Map; import java.util.Map.Entry; -import org.metafacture.commons.ResourceUtil; -import org.metafacture.commons.reflection.ObjectFactory; -import org.metafacture.framework.MetafactureException; -import org.metafacture.framework.annotations.Description; -import org.metafacture.framework.annotations.In; -import org.metafacture.framework.annotations.Out; -import org.metafacture.framework.annotations.ReturnsAvailableArguments; - /** - * prints Flux help for a given {@link ObjectFactory} + * Prints Flux help for a given {@link ObjectFactory} * * @author Markus Michael Geipel */ public final class HelpPrinter { + private HelpPrinter() { // no instances } + /** + * Prints Flux help to the standard output. + * + * @param args unused + * + * @see #print + */ + public static void main(final String[] args) { + FluxProgramm.printHelp(System.out); + } + + /** + * Prints Flux help for a given ObjectFactory. Excerpts setters and their + * arguments, {@code @in} annotations and {@code @out} annotations. + * + * @param factory the ObjectFactory + * @param out the PrintStream to print to + */ public static void print(final ObjectFactory factory, final PrintStream out) { out.println("Welcome to Metafacture"); @@ -58,7 +80,7 @@ public static void print(final ObjectFactory factory, final List keyWords = new ArrayList(); keyWords.addAll(factory.keySet()); Collections.sort(keyWords); - for (String name : keyWords) { + for (final String name : keyWords) { describe(name, factory, out); } } @@ -66,20 +88,19 @@ public static void print(final ObjectFactory factory, private static String getVersionInfo() { try { return ResourceUtil.loadProperties("build.properties").toString(); - } catch (IOException e) { + } + catch (final IOException e) { throw new MetafactureException("Failed to load build infos", e); } } - private static void describe(String name, ObjectFactory factory, - PrintStream out) { - final Class moduleClass = factory.get(name).getPlainClass(); + private static void describe(final String name, final ObjectFactory factory, final PrintStream out) { // checkstyle-disable-line ExecutableStatementCount + final ConfigurableClass configurableClass = factory.get(name); + final Class moduleClass = configurableClass.getPlainClass(); final Description desc = moduleClass.getAnnotation(Description.class); out.println(name); - name.chars().forEach(c-> { - out.print("-"); - }); + name.chars().forEach(c -> out.print("-")); out.println(); if (desc != null) { @@ -90,21 +111,29 @@ private static void describe(String name, ObjectFactory factory, out.println("- arguments:\t" + arguments); } - final Map> attributes = factory.get(name).getSetterTypes(); + final Map attributes = configurableClass.getSetters(); if (!attributes.isEmpty()) { out.print("- options:\t"); final StringBuilder builder = new StringBuilder(); - for (Entry> entry : attributes.entrySet()) { - if (entry.getValue().isEnum()) { + for (final Entry entry : attributes.entrySet()) { + final Method method = entry.getValue(); + final Class type = configurableClass.getSetterType(method); + + if (method.isAnnotationPresent(Deprecated.class)) { + builder.append("[deprecated] "); + } + + if (type.isEnum()) { builder.append(entry.getKey()) .append(" ") - .append(Arrays.asList(entry.getValue().getEnumConstants())) + .append(Arrays.asList(type.getEnumConstants())) .append(", "); - } else { + } + else { builder.append(entry.getKey()) .append(" (") - .append(entry.getValue().getSimpleName()) + .append(type.getSimpleName()) .append("), "); } @@ -128,14 +157,13 @@ private static void describe(String name, ObjectFactory factory, } @SuppressWarnings("unchecked") - private static Collection getAvailableArguments( - Class moduleClass) { - for (Method method : moduleClass.getMethods()) { + private static Collection getAvailableArguments(final Class moduleClass) { + for (final Method method : moduleClass.getMethods()) { if (method.getAnnotation(ReturnsAvailableArguments.class) != null) { try { return (Collection) method.invoke(moduleClass); - } catch (IllegalAccessException | InvocationTargetException | - IllegalArgumentException e) { + } + catch (final IllegalAccessException | InvocationTargetException | IllegalArgumentException e) { // silently ignore } } diff --git a/metafacture-flux/src/main/java/org/metafacture/flux/parser/Flow.java b/metafacture-flux/src/main/java/org/metafacture/flux/parser/Flow.java index b171f4843..84a7b5de3 100644 --- a/metafacture-flux/src/main/java/org/metafacture/flux/parser/Flow.java +++ b/metafacture-flux/src/main/java/org/metafacture/flux/parser/Flow.java @@ -13,12 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.flux.parser; -import java.util.ArrayList; -import java.util.Deque; -import java.util.LinkedList; -import java.util.List; +package org.metafacture.flux.parser; import org.metafacture.flux.FluxParseException; import org.metafacture.framework.LifeCycle; @@ -28,6 +24,10 @@ import org.metafacture.framework.Tee; import org.metafacture.io.StdInOpener; +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; /** * @author Markus Michael Geipel @@ -42,9 +42,12 @@ final class Flow { private ObjectReceiver start; private boolean joinLooseEnds; - @SuppressWarnings({ "unchecked", "rawtypes" }) + Flow() { + } + + @SuppressWarnings("unchecked") public void addElement(final Receiver nextElement) { - if(element==null){ + if (element == null) { setStart((ObjectReceiver) nextElement); return; } @@ -55,20 +58,24 @@ public void addElement(final Receiver nextElement) { for (final LifeCycle looseEnd : looseEndsStack.pop()) { if (looseEnd instanceof Tee) { ((Tee) looseEnd).addReceiver(nextElement); - } else { + } + else { ((Sender) looseEnd).setReceiver(nextElement); } } joinLooseEnds = false; - } else { + } + else { if (sender instanceof Tee) { ((Tee) sender).addReceiver(nextElement); - } else { + } + else { sender.setReceiver(nextElement); } } - } else { - throw new FluxParseException(element.getClass().getCanonicalName() + "is not a sender"); + } + else { + throw new FluxParseException(element.getClass().getCanonicalName() + " is not a sender"); } element = nextElement; } @@ -78,7 +85,8 @@ public void startTee() { final Tee tee = (Tee) element; teeStack.push(tee); looseEndsStack.push(new ArrayList()); - } else { + } + else { throw new FluxParseException("Flow cannot be split without a tee-element."); } } @@ -93,7 +101,7 @@ public void endSubFlow() { element = teeStack.peek(); } - private void setStart(final ObjectReceiver start){ + private void setStart(final ObjectReceiver start) { this.start = start; element = start; } diff --git a/metafacture-flux/src/main/java/org/metafacture/flux/parser/FluxProgramm.java b/metafacture-flux/src/main/java/org/metafacture/flux/parser/FluxProgramm.java index 72d13be0a..6c3aa98ad 100644 --- a/metafacture-flux/src/main/java/org/metafacture/flux/parser/FluxProgramm.java +++ b/metafacture-flux/src/main/java/org/metafacture/flux/parser/FluxProgramm.java @@ -13,8 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flux.parser; +import org.metafacture.commons.ResourceUtil; +import org.metafacture.commons.reflection.ConfigurableClass; +import org.metafacture.commons.reflection.ObjectFactory; +import org.metafacture.commons.reflection.ReflectionUtil; +import org.metafacture.flux.FluxParseException; +import org.metafacture.flux.HelpPrinter; +import org.metafacture.framework.Receiver; + import java.io.IOException; import java.io.PrintStream; import java.net.URL; @@ -27,14 +36,6 @@ import java.util.Map; import java.util.Set; -import org.metafacture.commons.ResourceUtil; -import org.metafacture.commons.reflection.ConfigurableClass; -import org.metafacture.commons.reflection.ObjectFactory; -import org.metafacture.commons.reflection.ReflectionUtil; -import org.metafacture.flux.FluxParseException; -import org.metafacture.flux.HelpPrinter; -import org.metafacture.framework.Receiver; - /** * @author Markus Michael Geipel * @@ -52,7 +53,8 @@ public final class FluxProgramm { final URL url = enumeration.nextElement(); COMMAND_FACTORY.loadClassesFromMap(ResourceUtil.loadProperties(url), Receiver.class); } - } catch (final IOException e) { + } + catch (final IOException e) { throw new FluxParseException("unable to load properties.", e); } } @@ -62,6 +64,12 @@ public final class FluxProgramm { private final Map wormholeNameMapping = new HashMap(); private final Map wormholeInFlowMapping = new Hashtable(); + /** + * Creates an instance of {@link FluxProgramm}. + */ + public FluxProgramm() { + } + private static Receiver createElement(final String name, final Map namedArgs, final List cArgs) { @@ -69,7 +77,8 @@ private static Receiver createElement(final String name, final Map elementClass = ReflectionUtil.loadClass(name, Receiver.class); newElement = elementClass.newInstance(namedArgs, cArgs.toArray()); @@ -151,17 +160,26 @@ protected void compile() { } } + /** + * Starts all flows. + */ public void start() { for (final Flow flow : initialFlows) { flow.start(); if (!wormholeInFlowMapping.containsKey(flow)) { flow.close(); - } else { + } + else { wormholeInFlowMapping.get(flow).finished(flow); } } } + /** + * Prints the help to the given PrintStream. + * + * @param out the PrintStream to orint to + */ public static void printHelp(final PrintStream out) { HelpPrinter.print(COMMAND_FACTORY, out); } @@ -172,7 +190,7 @@ private static final class Wormhole { private Flow out; private final String name; - public Wormhole(final String name) { + Wormhole(final String name) { this.name = name; } diff --git a/metafacture-flux/src/main/java/org/metafacture/flux/parser/StringSender.java b/metafacture-flux/src/main/java/org/metafacture/flux/parser/StringSender.java index 1637a5d94..90260f38a 100644 --- a/metafacture-flux/src/main/java/org/metafacture/flux/parser/StringSender.java +++ b/metafacture-flux/src/main/java/org/metafacture/flux/parser/StringSender.java @@ -13,30 +13,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flux.parser; import org.metafacture.framework.ObjectReceiver; import org.metafacture.framework.helpers.DefaultObjectPipe; - /** * Helper class to start a pipe with a {@link String} * * @author Markus Michael Geipel */ -public final class StringSender extends DefaultObjectPipe>{ +public final class StringSender extends DefaultObjectPipe> { private final String string; + /** + * Creates an instance of {@link StringSender} with the given string. + * + * @param string the string + */ public StringSender(final String string) { this.string = string; } @Override public void process(final Object notUsed) { - if(notUsed==null){ - getReceiver().process(string); - }else{ + if (notUsed == null) { + getReceiver().process(string); + } + else { throw new IllegalArgumentException("Parameter not used. Must be null"); } } diff --git a/metafacture-flux/src/test/java/org/metafacture/flux/FluxGrammarTest.java b/metafacture-flux/src/test/java/org/metafacture/flux/FluxGrammarTest.java index 0a833fc87..297e79653 100644 --- a/metafacture-flux/src/test/java/org/metafacture/flux/FluxGrammarTest.java +++ b/metafacture-flux/src/test/java/org/metafacture/flux/FluxGrammarTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flux; import static java.util.Collections.emptyMap; @@ -27,6 +28,7 @@ import org.antlr.runtime.RecognitionException; import org.junit.Before; import org.junit.Test; +import org.metafacture.commons.reflection.ReflectionException; import org.metafacture.flux.parser.FluxProgramm; /** @@ -53,7 +55,7 @@ public void setup() { @Test public void shouldAllowEmptyCommentInLastLineOfFile() throws RecognitionException, IOException { - final String script = "\"test\"|write(\"stdout\"); //"; + final String script = "\"test\"|print; //"; FluxCompiler.compile(createInputStream(script), emptyMap()); @@ -64,7 +66,7 @@ public void shouldAllowEmptyCommentInLastLineOfFile() @Test public void shouldAllowEmptyCommentInFile() throws RecognitionException, IOException { - final String script = "\"test\"|write(\"stdout\"); //\n"; + final String script = "\"test\"|print; //\n"; FluxCompiler.compile(createInputStream(script), emptyMap()); @@ -77,7 +79,7 @@ public void shouldReplaceJavaEscapeSequences() throws IOException, RecognitionException { final String script = "\"quot=\\\" octal1=\\7 octal2=\\60 octal3=\\103 unicode=\\u00f8 tab=[\\t]\"" + - "|write(\"stdout\");"; + "|print;"; final FluxProgramm program = FluxCompiler.compile( createInputStream(script), emptyMap()); @@ -88,6 +90,103 @@ public void shouldReplaceJavaEscapeSequences() stdoutBuffer.toString()); } + @Test(expected = FluxParseException.class) + public void issue421_shouldThrowFluxParseExceptionWhenSemicolonInFlowIsMissing() + throws RecognitionException, IOException { + final String script = "\"test\"|print"; + try { + FluxCompiler.compile(createInputStream(script), emptyMap()); + } catch (FluxParseException fpe) { + assertEquals("mismatched input '' expecting ';' in Flux", fpe.getMessage()); + throw fpe; + } + } + + @Test(expected = FluxParseException.class) + public void issue421_shouldThrowFluxParseExceptionWhenSemicolonInVarDefIsMissing() + throws RecognitionException, IOException { + final String script = "foo=42"; + try { + FluxCompiler.compile(createInputStream(script), emptyMap()); + } catch (FluxParseException re) { + assertEquals("mismatched input '' expecting ';' in Flux", re.getMessage()); + throw re; + } + } + + @Test(expected = ReflectionException.class) + public void issue421_shouldThrowReflectionExceptionWhenCommandIsNotFound() + throws RecognitionException, IOException { + final String script = "\"test\"|prin;"; + try { + FluxCompiler.compile(createInputStream(script), emptyMap()); + } catch (ReflectionException re) { + assertEquals("Class not found: prin", re.getMessage()); + throw re; + } + } + + @Test(expected = FluxParseException.class) + public void issue421_shouldThrowFluxParseExceptionWhenInputIsMissingAfterPipe1() + throws RecognitionException, IOException { + final String script = "\"test\"|"; + try { + FluxCompiler.compile(createInputStream(script), emptyMap()); + } catch (FluxParseException re) { + assertEquals("no viable alternative at input '' in Flux", re.getMessage()); + throw re; + } + } + + @Test(expected = FluxParseException.class) + public void issue421_shouldThrowFluxParseExceptionWhenInputIsMissingAfterPipe2() + throws RecognitionException, IOException { + final String script = "\"test\"|;"; + try { + FluxCompiler.compile(createInputStream(script), emptyMap()); + } catch (FluxParseException re) { + assertEquals("no viable alternative at input ';' in Flux", re.getMessage()); + throw re; + } + } + + @Test(expected = FluxParseException.class) + public void issue421_shouldThrowFluxParseExceptionWhenTeeStructureOccursWithouATeeCommand() + throws RecognitionException, IOException { + final String script = "\"test\"|{print}{print} ;"; + try { + FluxCompiler.compile(createInputStream(script), emptyMap()); + } catch (FluxParseException re) { + assertEquals("Flow cannot be split without a tee-element.", re.getMessage()); + throw re; + } + } + + @Test(expected = FluxParseException.class) + public void issue421_shouldThrowFluxParseExceptionWhenTeeIsNotASender() + throws RecognitionException, IOException { + final String script = "\"test\"|print|object-tee|{print}{print} ;"; + try { + FluxCompiler.compile(createInputStream(script), emptyMap()); + } catch (FluxParseException re) { + assertEquals("org.metafacture.io.ObjectStdoutWriter is not a sender", re.getMessage()); + throw re; + } + } + + @Test(expected = FluxParseException.class) + public void issue421_shouldInsertMissingSymbolsWhenTeeIsStructurallyInvalid() + throws RecognitionException, IOException { + final String script = "\"test\"|object-tee|{object-tee{print{print} ;"; + try { + FluxCompiler.compile(createInputStream(script), emptyMap()); + String tmp=stdoutBuffer.toString(); + } catch (FluxParseException re) { + assertEquals("missing '}' at '{' in Flux", re.getMessage()); + throw re; + } + } + private ByteArrayInputStream createInputStream(String script) { return new ByteArrayInputStream(script.getBytes(StandardCharsets.UTF_8)); } diff --git a/metafacture-flux/src/test/java/org/metafacture/flux/FluxProgrammTest.java b/metafacture-flux/src/test/java/org/metafacture/flux/FluxProgrammTest.java index cc9aebc69..482764d58 100644 --- a/metafacture-flux/src/test/java/org/metafacture/flux/FluxProgrammTest.java +++ b/metafacture-flux/src/test/java/org/metafacture/flux/FluxProgrammTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.flux; import java.io.IOException; diff --git a/metafacture-formatting/src/main/java/org/metafacture/formatting/ObjectTemplate.java b/metafacture-formatting/src/main/java/org/metafacture/formatting/ObjectTemplate.java index e0783a223..0265fd3f2 100644 --- a/metafacture-formatting/src/main/java/org/metafacture/formatting/ObjectTemplate.java +++ b/metafacture-formatting/src/main/java/org/metafacture/formatting/ObjectTemplate.java @@ -13,12 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.formatting; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +package org.metafacture.formatting; import org.metafacture.commons.StringUtil; import org.metafacture.framework.FluxCommand; @@ -29,11 +25,15 @@ import org.metafacture.framework.helpers.DefaultObjectPipe; import org.metafacture.framework.objects.Triple; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** - * Builds a {@link String} from a template and an {@link Object}. ${o} marks - * the place where the object is to be inserted. If the received object in an - * instance of Triple ${s}, ${p} and ${o} are used instead. + * Builds a {@link String} from a template and an {@link Object}. `${o}` marks + * the place where the object is to be inserted. If the received object is an + * instance of Triple`${s}`, `${p}` and `${o}` are used instead. * * @param * object type @@ -41,8 +41,8 @@ * @author Markus Geipel * */ -@Description("Builds a String from a template and an Object. Provide template in brackets. ${o} marks the place where the object is to be inserted. " + - "If the object in an instance of Triple ${s}, ${p} and ${o} are used instead") +@Description("Builds a String from a template and an Object. Provide template in brackets. `${o}` marks the place where the object is to be inserted. " + + "If the object is an instance of Triple `${s}`, `${p}` and `${o}` are used instead.") @In(Object.class) @Out(String.class) @FluxCommand("template") @@ -50,23 +50,28 @@ public final class ObjectTemplate extends DefaultObjectPipe vars = new HashMap(); + private final Map vars = new HashMap<>(); private final String template; + /** + * Creates an instance of {@link ObjectTemplate} with a given template. + * + * @param template the template + */ public ObjectTemplate(final String template) { - super(); this.template = template; } @Override public void process(final T obj) { - if(obj instanceof Triple){ - final Triple triple = (Triple)obj; + if (obj instanceof Triple) { + final Triple triple = (Triple) obj; vars.put("s", triple.getSubject()); vars.put("p", triple.getPredicate()); vars.put("o", triple.getObject()); getReceiver().process(StringUtil.format(template, vars)); - }else{ + } + else { final Matcher matcher = OBJ_PATTERN.matcher(template); getReceiver().process(matcher.replaceAll(obj.toString())); } diff --git a/metafacture-formatting/src/main/java/org/metafacture/formatting/PreambleEpilogueAdder.java b/metafacture-formatting/src/main/java/org/metafacture/formatting/PreambleEpilogueAdder.java index 3f7314f31..c740e9edf 100644 --- a/metafacture-formatting/src/main/java/org/metafacture/formatting/PreambleEpilogueAdder.java +++ b/metafacture-formatting/src/main/java/org/metafacture/formatting/PreambleEpilogueAdder.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formatting; import org.metafacture.framework.FluxCommand; @@ -22,7 +23,6 @@ import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultObjectPipe; - /** * Emits a preamble string before the first string object in the * stream and an epilogue string before the end of the stream. @@ -44,7 +44,13 @@ public final class PreambleEpilogueAdder extends DefaultObjectPipepreamble string which is emitted before the first object. @@ -61,6 +67,11 @@ public void setPreamble(final String preamble) { this.preamble = preamble; } + /** + * Gets the preamble. + * + * @return the preamble + */ public String getPreamble() { return preamble; } @@ -80,13 +91,18 @@ public void setEpilogue(final String epilogue) { this.epilogue = epilogue; } + /** + * Gets the epilogue. + * + * @return the epilogue + */ public String getEpilogue() { return epilogue; } @Override public void process(final String obj) { - if(!objectsReceived && !preamble.isEmpty()) { + if (!objectsReceived && !preamble.isEmpty()) { getReceiver().process(preamble); } objectsReceived = true; @@ -95,7 +111,7 @@ public void process(final String obj) { @Override protected void onCloseStream() { - if(objectsReceived && !epilogue.isEmpty()) { + if (objectsReceived && !epilogue.isEmpty()) { getReceiver().process(epilogue); } } diff --git a/metafacture-formatting/src/main/java/org/metafacture/formatting/StreamLiteralFormatter.java b/metafacture-formatting/src/main/java/org/metafacture/formatting/StreamLiteralFormatter.java index cf3e98337..43456b922 100644 --- a/metafacture-formatting/src/main/java/org/metafacture/formatting/StreamLiteralFormatter.java +++ b/metafacture-formatting/src/main/java/org/metafacture/formatting/StreamLiteralFormatter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formatting; import org.metafacture.framework.FluxCommand; @@ -45,8 +46,7 @@ @In(StreamReceiver.class) @Out(String.class) @FluxCommand("encode-literals") -public final class StreamLiteralFormatter - extends DefaultStreamPipe> { +public final class StreamLiteralFormatter extends DefaultStreamPipe> { /** * The default value for {@link #setSeparator(String)}. @@ -55,6 +55,12 @@ public final class StreamLiteralFormatter private String separator = DEFAULT_SEPARATOR; + /** + * Creates an instance of {@link StreamLiteralFormatter}. + */ + public StreamLiteralFormatter() { + } + /** * Sets the separator between the literal name and value. The separator is * only added if the literal name is not empty. @@ -70,6 +76,11 @@ public void setSeparator(final String separator) { this.separator = separator; } + /** + * Gets the separator. + * + * @return the separator + */ public String getSeparator() { return separator; } @@ -78,7 +89,8 @@ public String getSeparator() { public void literal(final String name, final String value) { if (name == null || name.isEmpty()) { getReceiver().process(value); - } else { + } + else { getReceiver().process(name + separator + value); } } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/Formeta.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/Formeta.java index b3b06c339..a48f6bb7c 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/Formeta.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/Formeta.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta; /** @@ -39,6 +40,12 @@ private Formeta() { // No instances allowed } + /** + * Checks whether a character is a whitespace. + * + * @param ch the character + * @return true if the character is a whitespace + */ public static boolean isWhitespace(final char ch) { return WHITESPACE.indexOf(ch) > -1; } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaDecoder.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaDecoder.java index f75692676..315cd52cb 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaDecoder.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaDecoder.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta; import org.metafacture.formeta.parser.Emitter; @@ -41,6 +42,9 @@ public final class FormetaDecoder extends private final FormetaParser parser = new FormetaParser(); private final Emitter emitter = new FullRecordEmitter(); + /** + * Creates an instance of {@link FormetaDecoder}. + */ public FormetaDecoder() { parser.setEmitter(emitter); } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaEncoder.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaEncoder.java index d6e8740f4..440686f50 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaEncoder.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaEncoder.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta; import org.metafacture.formeta.formatter.Formatter; @@ -35,22 +36,36 @@ @In(StreamReceiver.class) @Out(String.class) @FluxCommand("encode-formeta") -public final class FormetaEncoder extends - DefaultStreamPipe> { +public final class FormetaEncoder extends DefaultStreamPipe> { private FormatterStyle style = FormatterStyle.CONCISE; private Formatter formatter = style.createFormatter(); + /** + * Creates an instance of {@link FormetaEncoder}. + */ + public FormetaEncoder() { + } + + /** + * Gets the style. + * + * @return the {@link FormatterStyle} + */ public FormatterStyle getStyle() { return style; } + /** + * Sets the style of the Formatter. + * + * @param formatterStyle the {@link FormatterStyle} + */ public void setStyle(final FormatterStyle formatterStyle) { this.style = formatterStyle; formatter = formatterStyle.createFormatter(); } - @Override public void startRecord(final String identifier) { formatter.reset(); diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaRecordsReader.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaRecordsReader.java index 983527017..824fbc481 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaRecordsReader.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/FormetaRecordsReader.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.formeta; -import java.io.IOException; -import java.io.Reader; +package org.metafacture.formeta; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.MetafactureException; @@ -26,6 +24,9 @@ import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultObjectPipe; +import java.io.IOException; +import java.io.Reader; + /** * Reads a stream of formeta data and splits between each top-level element. * @@ -36,15 +37,21 @@ @Out(String.class) @Description("Reads a stream of formeta data and splits between each top-level element") @FluxCommand("as-formeta-records") -public final class FormetaRecordsReader extends - DefaultObjectPipe> { +public final class FormetaRecordsReader extends DefaultObjectPipe> { private static final int BUFFER_SIZE = 1024 * 1024 * 16; private final StringBuilder builder = new StringBuilder(); private final char[] buffer = new char[BUFFER_SIZE]; - @Override + /** + * Creates an instance of {@link FormetaRecordsReader}. + */ + public FormetaRecordsReader() { + } + + @Override // checkstyle-disable-line CyclomaticComplexity + @SuppressWarnings("fallthrough") public void process(final Reader reader) { assert !isClosed(); @@ -58,29 +65,32 @@ public void process(final Reader reader) { int offset = 0; for (int i = 0; i < size; ++i) { switch (buffer[i]) { - case Formeta.ESCAPE_CHAR: - i += 1; // Skip next character - break; - case Formeta.GROUP_START: - if (!inQuotedText) { - groupLevel += 1; - } - break; - case Formeta.GROUP_END: - if (!inQuotedText) { - groupLevel -= 1; - } - // Fall through - case Formeta.ITEM_SEPARATOR: - if (!inQuotedText && groupLevel == 0) { - builder.append(buffer, offset, i - offset + 1); - offset = i + 1; - emitRecord(); - } - break; - case Formeta.QUOT_CHAR: - inQuotedText = !inQuotedText; - break; + case Formeta.ESCAPE_CHAR: + // Skip next character + i += 1; // checkstyle-disable-line ModifiedControlVariable + break; + case Formeta.GROUP_START: + if (!inQuotedText) { + groupLevel += 1; + } + break; + case Formeta.GROUP_END: + if (!inQuotedText) { + groupLevel -= 1; + } + // fall through + case Formeta.ITEM_SEPARATOR: + if (!inQuotedText && groupLevel == 0) { + builder.append(buffer, offset, i - offset + 1); + offset = i + 1; + emitRecord(); + } + break; + case Formeta.QUOT_CHAR: + inQuotedText = !inQuotedText; + break; + default: + // ignore } } builder.append(buffer, offset, size - offset); @@ -89,7 +99,8 @@ public void process(final Reader reader) { emitRecord(); } - } catch (final IOException e) { + } + catch (final IOException e) { throw new MetafactureException(e); } } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/AbstractFormatter.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/AbstractFormatter.java index 14df23d1a..52ecf1daf 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/AbstractFormatter.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/AbstractFormatter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.formatter; import org.metafacture.commons.StringUtil; @@ -26,15 +27,15 @@ */ public abstract class AbstractFormatter implements Formatter { - public static final String CHARS_TO_ESCAPE_QUOTED = "\n\r" - + Formeta.QUOT_CHAR - + Formeta.ESCAPE_CHAR; + public static final String CHARS_TO_ESCAPE_QUOTED = "\n\r" + + Formeta.QUOT_CHAR + + Formeta.ESCAPE_CHAR; - public static final String CHARS_TO_ESCAPE = CHARS_TO_ESCAPE_QUOTED - + Formeta.GROUP_START - + Formeta.GROUP_END - + Formeta.ITEM_SEPARATOR - + Formeta.NAME_VALUE_SEPARATOR; + public static final String CHARS_TO_ESCAPE = CHARS_TO_ESCAPE_QUOTED + + Formeta.GROUP_START + + Formeta.GROUP_END + + Formeta.ITEM_SEPARATOR + + Formeta.NAME_VALUE_SEPARATOR; protected static final int BUFFER_SIZE = 1024; @@ -73,16 +74,17 @@ protected final void escapeAndAppend(final String str) { escapeAndAppendChar(buffer[i], CHARS_TO_ESCAPE_QUOTED); } builder.append(Formeta.QUOT_CHAR); - } else { + } + else { if (bufferLen > 0) { escapeAndAppendChar(buffer[0], CHARS_TO_ESCAPE + Formeta.WHITESPACE); } - for (int i = 1; i < bufferLen-1; ++i) { + for (int i = 1; i < bufferLen - 1; ++i) { escapeAndAppendChar(buffer[i], CHARS_TO_ESCAPE); } if (bufferLen > 1) { - escapeAndAppendChar(buffer[bufferLen-1], + escapeAndAppendChar(buffer[bufferLen - 1], CHARS_TO_ESCAPE + Formeta.WHITESPACE); } } @@ -92,12 +94,13 @@ protected void onReset() { // Default implementation does nothing } - protected abstract boolean shouldQuoteText(final char[] buffer, final int len); + protected abstract boolean shouldQuoteText(char[] currentBuffer, int len); private void escapeAndAppendChar(final char ch, final String charsToEscape) { if (charsToEscape.indexOf(ch) > -1) { appendEscapedChar(ch); - } else { + } + else { builder.append(ch); } } @@ -105,14 +108,14 @@ private void escapeAndAppendChar(final char ch, final String charsToEscape) { private void appendEscapedChar(final char ch) { builder.append(Formeta.ESCAPE_CHAR); switch (ch) { - case '\n': - builder.append(Formeta.NEWLINE_ESC_SEQ); - break; - case '\r': - builder.append(Formeta.CARRIAGE_RETURN_ESC_SEQ); - break; - default: - builder.append(ch); + case '\n': + builder.append(Formeta.NEWLINE_ESC_SEQ); + break; + case '\r': + builder.append(Formeta.CARRIAGE_RETURN_ESC_SEQ); + break; + default: + builder.append(ch); } } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/ConciseFormatter.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/ConciseFormatter.java index 0dbf831fc..b6b169a8c 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/ConciseFormatter.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/ConciseFormatter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.formatter; import org.metafacture.formeta.Formeta; @@ -27,6 +28,12 @@ public final class ConciseFormatter extends AbstractFormatter { private boolean appendItemSeparator; + /** + * Creates an instance of {@link ConciseFormatter}. + */ + public ConciseFormatter() { + } + @Override public void startGroup(final String name) { if (appendItemSeparator) { diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/Formatter.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/Formatter.java index 270abeddb..c5badd39f 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/Formatter.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/Formatter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.formatter; /** @@ -23,12 +24,29 @@ */ public interface Formatter { + /** + * The reset event. + */ void reset(); - void startGroup(final String name); + /** + * The startGroup event. + * + * @param name the name of the startGroup + */ + void startGroup(String name); + /** + * The literal endGroup event. + */ void endGroup(); - void literal(final String name, final String value); + /** + * The literal event. + * + * @param name the name of the literal + * @param value the value of the literal + */ + void literal(String name, String value); } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/FormatterStyle.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/FormatterStyle.java index fc6ce9190..6cbe73cc7 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/FormatterStyle.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/FormatterStyle.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.formatter; /** @@ -56,5 +57,12 @@ public Formatter createFormatter() { } }; + /** + * Create a Formatter with various output styles for formeta, + * + * @see Formatter + * @see org.metafacture.formeta.Formeta + * @return the Formatter + */ public abstract Formatter createFormatter(); } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/MultilineFormatter.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/MultilineFormatter.java index ded54f0a5..9dc4dd802 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/MultilineFormatter.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/MultilineFormatter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.formatter; import org.metafacture.formeta.Formeta; @@ -35,8 +36,10 @@ public final class MultilineFormatter extends AbstractFormatter { private boolean appendItemSeparator; private boolean firstItem; + /** + * Creates an instance of {@link MultilineFormatter}. + */ public MultilineFormatter() { - super(); onReset(); } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/VerboseFormatter.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/VerboseFormatter.java index 3e055188a..81f4de27d 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/VerboseFormatter.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/formatter/VerboseFormatter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.formatter; import org.metafacture.formeta.Formeta; @@ -32,6 +33,12 @@ public final class VerboseFormatter extends AbstractFormatter { private boolean appendItemSeparator; + /** + * Creates an instance of {@link VerboseFormatter}. + */ + public VerboseFormatter() { + } + @Override public void startGroup(final String name) { if (appendItemSeparator) { diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/Emitter.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/Emitter.java index 5736360a1..39f03a792 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/Emitter.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/Emitter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.parser; import org.metafacture.framework.StreamReceiver; @@ -25,12 +26,35 @@ */ public interface Emitter { - void setReceiver(final StreamReceiver receiver); + /** + * Sets the receiver. + * + * @param receiver the receiver + */ + void setReceiver(StreamReceiver receiver); - void startGroup(final String name, final int nestingLevel); + /** + * The startGroup event. + * + * @param name the name of the startGroup event + * @param nestingLevel the nesting level of the startGroup event + */ + void startGroup(String name, int nestingLevel); - void endGroup(final int nestingLevel); + /** + * The endGroup event. + * + * @param nestingLevel the nesting level + */ + void endGroup(int nestingLevel); - void literal(final String name, final String value, final int nestingLevel); + /** + * The literal event. + * + * @param name the name of the literal event + * @param value the value of the literal event + * @param nestingLevel the nesting level of the literal event + */ + void literal(String name, String value, int nestingLevel); } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/FormetaParser.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/FormetaParser.java index 2fcecd6ba..fc36f83f5 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/FormetaParser.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/FormetaParser.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.parser; import org.metafacture.commons.StringUtil; @@ -36,16 +37,37 @@ public final class FormetaParser { private char[] buffer = new char[BUFFER_SIZE]; private final StructureParserContext structureParserContext = new StructureParserContext(); + /** + * Creates an instance of {@link FormetaParser}. + */ + public FormetaParser() { + } + + /** + * Sets the Emitter. + * + * @param emitter the Emitter + */ public void setEmitter(final Emitter emitter) { structureParserContext.setEmitter(emitter); } + /** + * Gets the Emitter. + * + * @return the Emitter + */ public Emitter getEmitter() { return structureParserContext.getEmitter(); } + /** + * Parses the data. + * + * @param data the data + */ public void parse(final String data) { - assert structureParserContext.getEmitter() != null: "No emitter set"; + assert structureParserContext.getEmitter() != null : "No emitter set"; // According to http://stackoverflow.com/a/11876086 it is faster to copy // a string into a char array then to use charAt(): @@ -59,16 +81,16 @@ public void parse(final String data) { for (; i < bufferLen; ++i) { state = state.processChar(buffer[i], structureParserContext); } - } catch (final FormatException e) { - final String errorMsg = "Parsing error at position " - + (i + 1) + ": " - + getErrorSnippet(data, i) + ", " - + e.getMessage(); + } + catch (final FormatException e) { + final String errorMsg = "Parsing error at position " + + (i + 1) + ": " + getErrorSnippet(data, i) + ", " + e.getMessage(); throw new FormatException(errorMsg, e); } try { state.endOfInput(structureParserContext); - } catch (final FormatException e) { + } + catch (final FormatException e) { throw new FormatException("Parsing error: " + e.getMessage(), e); } } @@ -88,7 +110,8 @@ private static String getErrorSnippet(final String record, final int pos) { final int start = pos - SNIPPET_SIZE / 2; if (start < 0) { snippet.append(record.substring(0, pos)); - } else { + } + else { snippet.append(SNIPPET_ELLIPSIS); snippet.append(record.substring(start, pos)); } @@ -101,7 +124,8 @@ private static String getErrorSnippet(final String record, final int pos) { final int end = pos + SNIPPET_SIZE / 2; if (end > record.length()) { snippet.append(record.substring(pos + 1)); - } else { + } + else { snippet.append(record.substring(pos + 1, end)); snippet.append(SNIPPET_ELLIPSIS); } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/FullRecordEmitter.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/FullRecordEmitter.java index 2f166f299..e5cdfa741 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/FullRecordEmitter.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/FullRecordEmitter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.parser; import org.metafacture.framework.FormatException; @@ -28,6 +29,12 @@ public final class FullRecordEmitter implements Emitter { private StreamReceiver receiver; + /** + * Creates an instance of {@link FullRecordEmitter}. + */ + public FullRecordEmitter() { + } + @Override public void setReceiver(final StreamReceiver receiver) { this.receiver = receiver; @@ -37,7 +44,8 @@ public void setReceiver(final StreamReceiver receiver) { public void startGroup(final String name, final int nestingLevel) { if (nestingLevel == 0) { receiver.startRecord(name); - } else { + } + else { receiver.startEntity(name); } } @@ -46,7 +54,8 @@ public void startGroup(final String name, final int nestingLevel) { public void endGroup(final int nestingLevel) { if (nestingLevel == 0) { receiver.endRecord(); - } else { + } + else { receiver.endEntity(); } } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/PartialRecordEmitter.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/PartialRecordEmitter.java index 863c4a0dc..4709e92c3 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/PartialRecordEmitter.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/PartialRecordEmitter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.parser; import org.metafacture.framework.StreamReceiver; @@ -25,12 +26,26 @@ public final class PartialRecordEmitter implements Emitter { private StreamReceiver receiver; private String defaultName; + /** + * Creates an instance of {@link PartialRecordEmitter}. + */ + public PartialRecordEmitter() { + } - + /** + * Sets the default name. + * + * @param defaultName the default name + */ public void setDefaultName(final String defaultName) { this.defaultName = defaultName; } + /** + * Gets the default name. + * + * @return the default name + */ public String getDefaultName() { return defaultName; } @@ -44,7 +59,8 @@ public void setReceiver(final StreamReceiver receiver) { public void startGroup(final String name, final int nestingLevel) { if (nestingLevel == 0 && defaultName != null && name.isEmpty()) { receiver.startEntity(defaultName); - } else { + } + else { receiver.startEntity(name); } } @@ -58,7 +74,8 @@ public void endGroup(final int nestingLevel) { public void literal(final String name, final String value, final int nestingLevel) { if (nestingLevel == 0 && defaultName != null && name.isEmpty()) { receiver.literal(defaultName, value); - } else { + } + else { receiver.literal(name, value); } } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/StructureParserContext.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/StructureParserContext.java index ebe4aa6cf..65f78035a 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/StructureParserContext.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/StructureParserContext.java @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.formeta.parser; +package org.metafacture.formeta.parser; /** * Context of the record parser. It manages the text parser and the generation of @@ -31,6 +31,9 @@ class StructureParserContext { private String literalName; private int nestingLevel; + StructureParserContext() { + } + public void setEmitter(final Emitter emitter) { this.emitter = emitter; } diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/StructureParserState.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/StructureParserState.java index fa7c34aea..2784a0aa8 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/StructureParserState.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/StructureParserState.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.parser; import org.metafacture.formeta.Formeta; @@ -30,29 +31,29 @@ enum StructureParserState { protected StructureParserState delimiterReached(final char ch, final StructureParserContext ctx) { final StructureParserState newState; switch (ch) { - case Formeta.GROUP_START: - ctx.startGroup(); - newState = ITEM_NAME; - break; - case Formeta.NAME_VALUE_SEPARATOR: - ctx.startLiteral(); - newState = LITERAL_VALUE; - break; - case Formeta.ITEM_SEPARATOR: - if (!ctx.isTextEmpty()) { - throw new FormatException(getUnexpectedCharMsg(NAME_DELIMITER_EXPECTED, ch)); - } - newState = ITEM_NAME; - break; - case Formeta.GROUP_END: - if (!ctx.isTextEmpty() || !ctx.isNested()) { + case Formeta.GROUP_START: + ctx.startGroup(); + newState = ITEM_NAME; + break; + case Formeta.NAME_VALUE_SEPARATOR: + ctx.startLiteral(); + newState = LITERAL_VALUE; + break; + case Formeta.ITEM_SEPARATOR: + if (!ctx.isTextEmpty()) { + throw new FormatException(getUnexpectedCharMsg(NAME_DELIMITER_EXPECTED, ch)); + } + newState = ITEM_NAME; + break; + case Formeta.GROUP_END: + if (!ctx.isTextEmpty() || !ctx.isNested()) { + throw new FormatException(getUnexpectedCharMsg(NAME_DELIMITER_EXPECTED, ch)); + } + ctx.endGroup(); + newState = ITEM_NAME; + break; + default: throw new FormatException(getUnexpectedCharMsg(NAME_DELIMITER_EXPECTED, ch)); - } - ctx.endGroup(); - newState = ITEM_NAME; - break; - default: - throw new FormatException(getUnexpectedCharMsg(NAME_DELIMITER_EXPECTED, ch)); } return newState; } @@ -68,20 +69,20 @@ public void endOfInput(final StructureParserContext ctx) { protected StructureParserState delimiterReached(final char ch, final StructureParserContext ctx) { final StructureParserState newState; switch (ch) { - case Formeta.ITEM_SEPARATOR: - ctx.endLiteral(); - newState = ITEM_NAME; - break; - case Formeta.GROUP_END: - if (!ctx.isNested()) { - throw new FormatException(getUnexpectedCharMsg(ITEM_SEPARATOR_EXPECTED, ch)); - } - ctx.endLiteral(); - ctx.endGroup(); - newState = ITEM_NAME; - break; - default: - throw new FormatException(getUnexpectedCharMsg(VALUE_DELIMITER_EXPECTED, ch)); + case Formeta.ITEM_SEPARATOR: + ctx.endLiteral(); + newState = ITEM_NAME; + break; + case Formeta.GROUP_END: + if (!ctx.isNested()) { + throw new FormatException(getUnexpectedCharMsg(ITEM_SEPARATOR_EXPECTED, ch)); + } + ctx.endLiteral(); + ctx.endGroup(); + newState = ITEM_NAME; + break; + default: + throw new FormatException(getUnexpectedCharMsg(VALUE_DELIMITER_EXPECTED, ch)); } return newState; } @@ -107,9 +108,9 @@ public StructureParserState processChar(final char ch, final StructureParserCont return this; } - public abstract void endOfInput(final StructureParserContext ctx); + public abstract void endOfInput(StructureParserContext ctx); - protected abstract StructureParserState delimiterReached(final char ch, final StructureParserContext ctx); + protected abstract StructureParserState delimiterReached(char ch, StructureParserContext ctx); private static String getUnexpectedCharMsg(final String expected, final char actual) { return expected + " expected but got '" + actual + "'"; diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/TextParserContext.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/TextParserContext.java index 709f93e23..08ce74e21 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/TextParserContext.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/TextParserContext.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.parser; import org.metafacture.formeta.Formeta; @@ -25,19 +26,22 @@ */ class TextParserContext { - private static final String ESCAPABLE_CHARS = Formeta.WHITESPACE - + Formeta.QUOT_CHAR - + Formeta.ESCAPE_CHAR - + Formeta.GROUP_START - + Formeta.GROUP_END - + Formeta.ITEM_SEPARATOR - + Formeta.NAME_VALUE_SEPARATOR; + private static final String ESCAPABLE_CHARS = Formeta.WHITESPACE + + Formeta.QUOT_CHAR + + Formeta.ESCAPE_CHAR + + Formeta.GROUP_START + + Formeta.GROUP_END + + Formeta.ITEM_SEPARATOR + + Formeta.NAME_VALUE_SEPARATOR; private final StringBuilder text = new StringBuilder(); private int lengthWithoutTrailingWs; private boolean quoted; + TextParserContext() { + } + public String getText() { return text.substring(0, lengthWithoutTrailingWs); } @@ -56,11 +60,14 @@ public void appendChar(final char ch) { public void appendEscapedChar(final char ch) { if (Formeta.NEWLINE_ESC_SEQ == ch) { text.append('\n'); - } else if (Formeta.CARRIAGE_RETURN_ESC_SEQ == ch) { + } + else if (Formeta.CARRIAGE_RETURN_ESC_SEQ == ch) { text.append('\r'); - } else if (ESCAPABLE_CHARS.indexOf(ch) > -1) { + } + else if (ESCAPABLE_CHARS.indexOf(ch) > -1) { text.append(ch); - } else { + } + else { throw new FormatException("invalid escape sequence: " + ch); } lengthWithoutTrailingWs = text.length(); diff --git a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/TextParserState.java b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/TextParserState.java index 905640692..0c2fc1a71 100644 --- a/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/TextParserState.java +++ b/metafacture-formeta/src/main/java/org/metafacture/formeta/parser/TextParserState.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.parser; import org.metafacture.formeta.Formeta; @@ -28,30 +29,31 @@ enum TextParserState { LEADING_WHITESPACE { public TextParserState processChar(final char ch, final TextParserContext ctx) { final TextParserState newState; - switch(ch) { - case Formeta.ESCAPE_CHAR: - ctx.setQuoted(false); - newState = ESCAPE_SEQ; - break; - case Formeta.QUOT_CHAR: - ctx.setQuoted(true); - newState = QUOTED_TEXT; - break; - case Formeta.GROUP_START: - case Formeta.GROUP_END: - case Formeta.ITEM_SEPARATOR: - case Formeta.NAME_VALUE_SEPARATOR: - ctx.setQuoted(false); - newState = DELIMITER_REACHED; - break; - default: - if (Formeta.isWhitespace(ch)) { - newState = LEADING_WHITESPACE; - } else { + switch (ch) { + case Formeta.ESCAPE_CHAR: ctx.setQuoted(false); - ctx.appendChar(ch); - newState = TEXT; - } + newState = ESCAPE_SEQ; + break; + case Formeta.QUOT_CHAR: + ctx.setQuoted(true); + newState = QUOTED_TEXT; + break; + case Formeta.GROUP_START: + case Formeta.GROUP_END: + case Formeta.ITEM_SEPARATOR: + case Formeta.NAME_VALUE_SEPARATOR: + ctx.setQuoted(false); + newState = DELIMITER_REACHED; + break; + default: + if (Formeta.isWhitespace(ch)) { + newState = LEADING_WHITESPACE; + } + else { + ctx.setQuoted(false); + ctx.appendChar(ch); + newState = TEXT; + } } return newState; } @@ -59,19 +61,19 @@ public TextParserState processChar(final char ch, final TextParserContext ctx) { TEXT { public TextParserState processChar(final char ch, final TextParserContext ctx) { final TextParserState newState; - switch(ch) { - case Formeta.ESCAPE_CHAR: - newState = ESCAPE_SEQ; - break; - case Formeta.GROUP_START: - case Formeta.GROUP_END: - case Formeta.ITEM_SEPARATOR: - case Formeta.NAME_VALUE_SEPARATOR: - newState = DELIMITER_REACHED; - break; - default: - ctx.appendChar(ch); - newState = TEXT; + switch (ch) { + case Formeta.ESCAPE_CHAR: + newState = ESCAPE_SEQ; + break; + case Formeta.GROUP_START: + case Formeta.GROUP_END: + case Formeta.ITEM_SEPARATOR: + case Formeta.NAME_VALUE_SEPARATOR: + newState = DELIMITER_REACHED; + break; + default: + ctx.appendChar(ch); + newState = TEXT; } return newState; } @@ -89,16 +91,16 @@ public void endOfInput(final TextParserContext ctx) { QUOTED_TEXT { public TextParserState processChar(final char ch, final TextParserContext ctx) { final TextParserState newState; - switch(ch) { - case Formeta.ESCAPE_CHAR: - newState = QUOTED_ESCAPE_SEQ; - break; - case Formeta.QUOT_CHAR: - newState = TRAILING_WHITESPACE; - break; - default: - ctx.appendChar(ch); - newState = QUOTED_TEXT; + switch (ch) { + case Formeta.ESCAPE_CHAR: + newState = QUOTED_ESCAPE_SEQ; + break; + case Formeta.QUOT_CHAR: + newState = TRAILING_WHITESPACE; + break; + default: + ctx.appendChar(ch); + newState = QUOTED_TEXT; } return newState; } @@ -120,25 +122,26 @@ public void endOfInput(final TextParserContext ctx) { TRAILING_WHITESPACE { public TextParserState processChar(final char ch, final TextParserContext ctx) { final TextParserState newState; - switch(ch) { - case Formeta.GROUP_START: - case Formeta.GROUP_END: - case Formeta.ITEM_SEPARATOR: - case Formeta.NAME_VALUE_SEPARATOR: - newState = DELIMITER_REACHED; - break; - default: - if (Formeta.isWhitespace(ch)) { - newState = TRAILING_WHITESPACE; - } else { - final String sep = "', '"; - final String expected = "whitespace or one of '" - + Formeta.GROUP_START + sep - + Formeta.GROUP_END + sep - + Formeta.ITEM_SEPARATOR + sep - + Formeta.NAME_VALUE_SEPARATOR + "'"; - throw new FormatException(getUnexpectedCharMsg(expected, ch)); - } + switch (ch) { + case Formeta.GROUP_START: + case Formeta.GROUP_END: + case Formeta.ITEM_SEPARATOR: + case Formeta.NAME_VALUE_SEPARATOR: + newState = DELIMITER_REACHED; + break; + default: + if (Formeta.isWhitespace(ch)) { + newState = TRAILING_WHITESPACE; + } + else { + final String sep = "', '"; + final String expected = "whitespace or one of '" + + Formeta.GROUP_START + sep + + Formeta.GROUP_END + sep + + Formeta.ITEM_SEPARATOR + sep + + Formeta.NAME_VALUE_SEPARATOR + "'"; + throw new FormatException(getUnexpectedCharMsg(expected, ch)); + } } return newState; } @@ -149,7 +152,7 @@ public TextParserState processChar(final char ch, final TextParserContext ctx) { } }; - public abstract TextParserState processChar(final char ch, final TextParserContext ctx); + public abstract TextParserState processChar(char ch, TextParserContext ctx); public void endOfInput(final TextParserContext ctx) { // Default implementation does nothing diff --git a/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaDecoderTest.java b/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaDecoderTest.java index 17f05af72..462ac4822 100644 --- a/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaDecoderTest.java +++ b/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaDecoderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta; import static org.mockito.Mockito.inOrder; diff --git a/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaEncoderTest.java b/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaEncoderTest.java index ff34b32de..5d2872243 100644 --- a/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaEncoderTest.java +++ b/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaEncoderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta; import static org.mockito.Mockito.verify; diff --git a/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaRecordsReaderTest.java b/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaRecordsReaderTest.java index cf5fea9ac..ba8b9f2c0 100644 --- a/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaRecordsReaderTest.java +++ b/metafacture-formeta/src/test/java/org/metafacture/formeta/FormetaRecordsReaderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta; import static org.mockito.Mockito.verify; diff --git a/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/AbstactFormatterTest.java b/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/AbstactFormatterTest.java index f3e38048f..d28ddcbc5 100644 --- a/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/AbstactFormatterTest.java +++ b/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/AbstactFormatterTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.formatter; import static org.junit.Assert.assertEquals; diff --git a/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/ConciseFormatterTest.java b/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/ConciseFormatterTest.java index cb2ed3904..cc5537d0b 100644 --- a/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/ConciseFormatterTest.java +++ b/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/ConciseFormatterTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.formatter; import static org.junit.Assert.assertEquals; diff --git a/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/MultilineFormatterTest.java b/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/MultilineFormatterTest.java index 9a5838a04..2252cff8e 100644 --- a/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/MultilineFormatterTest.java +++ b/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/MultilineFormatterTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.formatter; import static org.junit.Assert.assertEquals; diff --git a/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/VerboseFormatterTest.java b/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/VerboseFormatterTest.java index f519b55ae..abc5a5ce7 100644 --- a/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/VerboseFormatterTest.java +++ b/metafacture-formeta/src/test/java/org/metafacture/formeta/formatter/VerboseFormatterTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.formatter; import static org.junit.Assert.assertEquals; diff --git a/metafacture-formeta/src/test/java/org/metafacture/formeta/parser/FormetaParserTest.java b/metafacture-formeta/src/test/java/org/metafacture/formeta/parser/FormetaParserTest.java index 7b346d3ab..6bf5cf3b8 100644 --- a/metafacture-formeta/src/test/java/org/metafacture/formeta/parser/FormetaParserTest.java +++ b/metafacture-formeta/src/test/java/org/metafacture/formeta/parser/FormetaParserTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.formeta.parser; import static org.mockito.Mockito.inOrder; @@ -141,7 +142,8 @@ public void testShouldRecoverAfterIncompleteRecord() { // Try processing an incomplete record: try { parser.parse(BROKEN_RECORD); - } catch (FormatException e) { + } + catch (FormatException e) { // The decoder should recover automatically } diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/FluxCommand.java b/metafacture-framework/src/main/java/org/metafacture/framework/FluxCommand.java index 9d5e2bbec..29cc55fab 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/FluxCommand.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/FluxCommand.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; import java.lang.annotation.Documented; diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/FormatException.java b/metafacture-framework/src/main/java/org/metafacture/framework/FormatException.java index 50a000afb..ab09d71c6 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/FormatException.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/FormatException.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; /** @@ -25,10 +26,21 @@ public class FormatException extends MetafactureException { private static final long serialVersionUID = 0L; + /** + * Creates an instance of {@link FormatException} by given message + * + * @param message the message + */ public FormatException(final String message) { super(message); } + /** + * Creates an instance of {@link FormatException} by given message and cause. + * + * @param message the message + * @param cause the {@link Throwable} + */ public FormatException(final String message, final Throwable cause) { super(message, cause); } diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/LifeCycle.java b/metafacture-framework/src/main/java/org/metafacture/framework/LifeCycle.java index 964b8f5b9..c366dac1f 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/LifeCycle.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/LifeCycle.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; /** diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/MetafactureException.java b/metafacture-framework/src/main/java/org/metafacture/framework/MetafactureException.java index e1ef616ca..891dd6739 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/MetafactureException.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/MetafactureException.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; /** @@ -24,14 +25,31 @@ public class MetafactureException extends RuntimeException { private static final long serialVersionUID = 0L; + /** + * Creates an instance of {@link MetafactureException} by given message. + * + * @param message the {@link Throwable} + */ public MetafactureException(final String message) { super(message); } + /** + * Creates an instance of {@link MetafactureException} by given cause. + * + * @param cause the {@link Throwable} + */ public MetafactureException(final Throwable cause) { super(cause); } + /** + * Creates an instance of {@link MetafactureException} by given message and + * cause. + * + * @param message the message + * @param cause the {@link Throwable} + */ public MetafactureException(final String message, final Throwable cause) { super(message, cause); } diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/MissingIdException.java b/metafacture-framework/src/main/java/org/metafacture/framework/MissingIdException.java index 60496f791..b17a5ff4d 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/MissingIdException.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/MissingIdException.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; /** @@ -25,10 +26,22 @@ public final class MissingIdException extends MetafactureException { private static final long serialVersionUID = 0L; + /** + * Creates an instance of {@link MissingIdException} by given message + * + * @param message the message + */ public MissingIdException(final String message) { super(message); } + /** + * Creates an instance of {@link MissingIdException} by given message and given + * cause of the exception. + * + * @param message the message + * @param cause the {@link Throwable} + */ public MissingIdException(final String message, final Throwable cause) { super(message, cause); } diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/ObjectPipe.java b/metafacture-framework/src/main/java/org/metafacture/framework/ObjectPipe.java index 7b00cd09d..e7e29cdea 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/ObjectPipe.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/ObjectPipe.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; import org.metafacture.framework.helpers.DefaultObjectPipe; diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/ObjectReceiver.java b/metafacture-framework/src/main/java/org/metafacture/framework/ObjectReceiver.java index ec307b304..2c8330aab 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/ObjectReceiver.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/ObjectReceiver.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; import org.metafacture.framework.helpers.DefaultObjectReceiver; diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/Receiver.java b/metafacture-framework/src/main/java/org/metafacture/framework/Receiver.java index 6fde12af0..d6a499c0e 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/Receiver.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/Receiver.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; /** diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/Sender.java b/metafacture-framework/src/main/java/org/metafacture/framework/Sender.java index 7a33f6037..b0c473395 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/Sender.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/Sender.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; import org.metafacture.framework.helpers.DefaultSender; diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/StandardEventNames.java b/metafacture-framework/src/main/java/org/metafacture/framework/StandardEventNames.java index 6c9dd7c8d..654844367 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/StandardEventNames.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/StandardEventNames.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; /** diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/StreamPipe.java b/metafacture-framework/src/main/java/org/metafacture/framework/StreamPipe.java index 24e71e5cf..633aa513e 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/StreamPipe.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/StreamPipe.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; import org.metafacture.framework.helpers.DefaultStreamPipe; diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/StreamReceiver.java b/metafacture-framework/src/main/java/org/metafacture/framework/StreamReceiver.java index 4030f7c2c..ddabd00f5 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/StreamReceiver.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/StreamReceiver.java @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; import org.metafacture.framework.helpers.DefaultStreamReceiver; diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/Tee.java b/metafacture-framework/src/main/java/org/metafacture/framework/Tee.java index 8af592b53..a684511c7 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/Tee.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/Tee.java @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.framework; +package org.metafacture.framework; import org.metafacture.framework.helpers.DefaultTee; @@ -75,6 +75,6 @@ public interface Tee extends Sender { * * @return reference to the tee for method chaining */ - Tee clearReceivers(); + Tee clearReceivers(); } diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/XmlPipe.java b/metafacture-framework/src/main/java/org/metafacture/framework/XmlPipe.java index d9ae63d84..a6868591e 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/XmlPipe.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/XmlPipe.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; import org.metafacture.framework.helpers.DefaultXmlPipe; diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/XmlReceiver.java b/metafacture-framework/src/main/java/org/metafacture/framework/XmlReceiver.java index ad83ea4c6..5b145a38b 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/XmlReceiver.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/XmlReceiver.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework; import org.metafacture.framework.helpers.DefaultXmlReceiver; + import org.xml.sax.ContentHandler; import org.xml.sax.DTDHandler; import org.xml.sax.EntityResolver; diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/annotations/Description.java b/metafacture-framework/src/main/java/org/metafacture/framework/annotations/Description.java index a5889ea4c..da812189e 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/annotations/Description.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/annotations/Description.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.annotations; import java.lang.annotation.Documented; @@ -21,14 +22,19 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - /** - * @author Markus Michael Geipel * Metadata for pipe elements + * + * @author Markus Michael Geipel */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Description { + /** + * Returns the description of the class. + * + * @return the description as String + */ String value(); } diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/annotations/In.java b/metafacture-framework/src/main/java/org/metafacture/framework/annotations/In.java index f025df675..aa04d4203 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/annotations/In.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/annotations/In.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.annotations; import java.lang.annotation.Documented; @@ -21,14 +22,19 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - /** - * @author Markus Michael Geipel * Metadata for pipe elements + * + * @author Markus Michael Geipel */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface In { + /** + * Returns the class expected as input when using the class in a pipe. + * + * @return the Class + */ Class value(); } diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/annotations/Out.java b/metafacture-framework/src/main/java/org/metafacture/framework/annotations/Out.java index 31fb5727c..d9eecc70f 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/annotations/Out.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/annotations/Out.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.annotations; import java.lang.annotation.Documented; @@ -21,14 +22,19 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - /** + * Metadata for pipe elements. + * * @author Markus Michael Geipel - * Metadata for pipe elements */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Out { + /** + * Returns the class produced as output when using this class in a pipe. + * + * @return the Class + */ Class value(); } diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/annotations/ReturnsAvailableArguments.java b/metafacture-framework/src/main/java/org/metafacture/framework/annotations/ReturnsAvailableArguments.java index fa32d9728..8ea902f35 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/annotations/ReturnsAvailableArguments.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/annotations/ReturnsAvailableArguments.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.annotations; import java.lang.annotation.Documented; diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultLifeCycle.java b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultLifeCycle.java index 14eaafef7..fc28a2206 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultLifeCycle.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultLifeCycle.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.helpers; import org.metafacture.framework.LifeCycle; @@ -26,6 +27,12 @@ */ public class DefaultLifeCycle implements LifeCycle { + /** + * Creates an instance of {@link DefaultLifeCycle}. + */ + public DefaultLifeCycle() { + } + @Override public void resetStream() { // Default implementation does nothing diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultObjectPipe.java b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultObjectPipe.java index 6ef27ffd9..d12cebeb3 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultObjectPipe.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultObjectPipe.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.helpers; import org.metafacture.framework.ObjectPipe; @@ -28,8 +29,13 @@ * @author Christoph Böhme * */ -public class DefaultObjectPipe - extends DefaultSender implements ObjectPipe { +public class DefaultObjectPipe extends DefaultSender implements ObjectPipe { + + /** + * Creates an instance of {@link DefaultObjectPipe}. + */ + public DefaultObjectPipe() { + } @Override public void process(final T obj) { diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultObjectReceiver.java b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultObjectReceiver.java index 8208580b4..dd23ae754 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultObjectReceiver.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultObjectReceiver.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.helpers; import org.metafacture.framework.ObjectPipe; @@ -33,6 +34,12 @@ */ public class DefaultObjectReceiver extends DefaultLifeCycle implements ObjectReceiver { + /** + * Creates an instance of {@link DefaultObjectReceiver}. + */ + public DefaultObjectReceiver() { + } + @Override public void process(final T obj) { // Default implementation does nothing diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultSender.java b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultSender.java index 50895599c..042c73ef3 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultSender.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultSender.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.helpers; import org.metafacture.framework.Receiver; @@ -38,15 +39,26 @@ public class DefaultSender implements Sender { private T receiver; private boolean isClosed; + /** + * Creates an instance of {@link DefaultSender}. + */ + public DefaultSender() { + } + + /** + * Checks whether the DefaultSender is closed. + * + * @return true if the DefaultSender is closed + */ public final boolean isClosed() { return isClosed; } @Override - public final R setReceiver(final R receiver) { - this.receiver = receiver; + public final R setReceiver(final R newReceiver) { + receiver = newReceiver; onSetReceiver(); - return receiver; + return newReceiver; } @Override diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultStreamPipe.java b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultStreamPipe.java index 3e5c0b164..913bc4ea2 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultStreamPipe.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultStreamPipe.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.helpers; import org.metafacture.framework.Receiver; @@ -27,8 +28,12 @@ * @author Christoph Böhme * @see ForwardingStreamPipe */ -public class DefaultStreamPipe - extends DefaultSender implements StreamPipe { +public class DefaultStreamPipe extends DefaultSender implements StreamPipe { + /** + * Creates an instance of {@link DefaultStreamPipe}. + */ + public DefaultStreamPipe() { + } @Override public void startRecord(final String identifier) { diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultStreamReceiver.java b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultStreamReceiver.java index 22b9d0471..fa4bf651b 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultStreamReceiver.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultStreamReceiver.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.helpers; import org.metafacture.framework.StreamPipe; @@ -30,6 +31,11 @@ * */ public class DefaultStreamReceiver extends DefaultLifeCycle implements StreamReceiver { + /** + * Creates an instance of {@link DefaultStreamReceiver}. + */ + public DefaultStreamReceiver() { + } @Override public void startRecord(final String identifier) { @@ -37,7 +43,7 @@ public void startRecord(final String identifier) { } @Override - public void endRecord(){ + public void endRecord() { // Default implementation does nothing } diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultTee.java b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultTee.java index 26cceadc4..d7ee4843e 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultTee.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultTee.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.framework.helpers; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +package org.metafacture.framework.helpers; import org.metafacture.framework.Receiver; import org.metafacture.framework.Tee; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * Default implementation for tee modules. @@ -34,6 +34,12 @@ public class DefaultTee implements Tee { private final List receivers = new ArrayList(); + /** + * Creates an instance of {@link DefaultTee}. + */ + public DefaultTee() { + } + @Override public final R setReceiver(final R receiver) { receivers.clear(); diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultXmlPipe.java b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultXmlPipe.java index 1345a17ae..894fea7fa 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultXmlPipe.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultXmlPipe.java @@ -13,18 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.framework.helpers; -import java.io.IOException; +package org.metafacture.framework.helpers; import org.metafacture.framework.Receiver; import org.metafacture.framework.XmlPipe; + import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; +import java.io.IOException; + /** * Default implementation for {@link XmlPipe}s which simply * does nothing. @@ -34,8 +36,18 @@ * @author Christoph Böhme * */ -public class DefaultXmlPipe - extends DefaultSender implements XmlPipe { +public class DefaultXmlPipe extends DefaultSender implements XmlPipe { + + public static final String DEFAULT_ATTRIBUTE_MARKER = ""; + public static final String DEFAULT_RECORD_TAG = "record"; + public static final String DEFAULT_ROOT_TAG = "records"; + public static final String DEFAULT_VALUE_TAG = "value"; + + /** + * Creates an instance of {@link DefaultXmlPipe}. + */ + public DefaultXmlPipe() { + } @Override public void setDocumentLocator(final Locator locator) { diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultXmlReceiver.java b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultXmlReceiver.java index 529bb36e1..00b1f3bf7 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultXmlReceiver.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/DefaultXmlReceiver.java @@ -13,18 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.framework.helpers; -import java.io.IOException; +package org.metafacture.framework.helpers; import org.metafacture.framework.XmlPipe; import org.metafacture.framework.XmlReceiver; + import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; +import java.io.IOException; + /** * Default implementation of {@link XmlReceiver} which * simply does nothing. Do not use this class as a base class @@ -38,6 +40,12 @@ */ public class DefaultXmlReceiver extends DefaultLifeCycle implements XmlReceiver { + /** + * Creates an instance of {@link DefaultXmlReceiver}. + */ + public DefaultXmlReceiver() { + } + @Override public void setDocumentLocator(final Locator locator) { // Default implementation does nothing diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/ForwardingStreamPipe.java b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/ForwardingStreamPipe.java index 8b2da1bf1..3e38ed59a 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/helpers/ForwardingStreamPipe.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/helpers/ForwardingStreamPipe.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.helpers; import org.metafacture.framework.StreamPipe; @@ -28,6 +29,12 @@ */ public class ForwardingStreamPipe extends DefaultStreamPipe { + /** + * Creates an instance of {@link ForwardingStreamPipe}. + */ + public ForwardingStreamPipe() { + } + @Override public void startRecord(final String identifier) { getReceiver().startRecord(identifier); diff --git a/metafacture-framework/src/main/java/org/metafacture/framework/objects/Triple.java b/metafacture-framework/src/main/java/org/metafacture/framework/objects/Triple.java index e6d216a7d..e6edd8205 100644 --- a/metafacture-framework/src/main/java/org/metafacture/framework/objects/Triple.java +++ b/metafacture-framework/src/main/java/org/metafacture/framework/objects/Triple.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.objects; import java.io.IOException; @@ -34,11 +35,26 @@ public final class Triple implements Comparable { private final int preCompHashCode; + /** + * Creates an instance of {@link Triple}. + * + * @param subject the subject + * @param predicate the predicate + * @param object the object + */ public Triple(final String subject, final String predicate, final String object) { this(subject, predicate, object, ObjectType.STRING); } + /** + * Constructs a Triple. + * + * @param subject the subject + * @param predicate the predicate + * @param object the object + * @param objectType the ObjectType + */ public Triple(final String subject, final String predicate, final String object, final ObjectType objectType) { this.subject = subject; @@ -52,31 +68,65 @@ private int computeHashCode() { return Objects.hash(subject, predicate, object, objectType); } + /** + * Gets the subject. + * + * @return the subject + */ public String getSubject() { return subject; } + /** + * Gets the predicate + * + * @return the predicate + */ public String getPredicate() { return predicate; } + /** + * Gets the object. + * + * @return the object + */ public String getObject() { return object; } + /** + * Gets the object type. + * + * @return the {@link ObjectType} + */ public ObjectType getObjectType() { return objectType; } + /** + * Reads an ObjectInputStream as Triple and returns it. + * + * @param in the ObjectInputStream + * @return the Triple + * @throws IOException if an I/O error occurs + */ public static Triple read(final ObjectInputStream in) throws IOException { try { return new Triple(in.readUTF(), in.readUTF(), in.readUTF(), (ObjectType) in.readObject()); - } catch (final ClassNotFoundException e) { + } + catch (final ClassNotFoundException e) { throw new IOException("Cannot read triple", e); } } + /** + * Writes the Triple to an ObjectOutputStream. + * + * @param out the ObjectOutputStream. + * @throws IOException if an I/O error occurs + */ public void write(final ObjectOutputStream out) throws IOException { out.writeUTF(subject); out.writeUTF(predicate); @@ -98,11 +148,11 @@ public boolean equals(final Object obj) { return false; } final Triple other = (Triple) obj; - return other.preCompHashCode == preCompHashCode - && other.predicate.equals(predicate) - && other.object.equals(object) - && other.subject.equals(subject) - && other.objectType == objectType; + return other.preCompHashCode == preCompHashCode && + other.predicate.equals(predicate) && + other.object.equals(object) && + other.subject.equals(subject) && + other.objectType == objectType; } @Override @@ -112,7 +162,7 @@ public int compareTo(final Triple triple) { result = predicate.compareTo(triple.predicate); if (result == 0) { result = object.compareTo(triple.object); - if(result == 0) { + if (result == 0) { return objectType.compareTo(triple.objectType); } } diff --git a/metafacture-framework/src/test/java/org/metafacture/framework/helpers/DefaultSenderTest.java b/metafacture-framework/src/test/java/org/metafacture/framework/helpers/DefaultSenderTest.java index b8e937450..2645e7032 100644 --- a/metafacture-framework/src/test/java/org/metafacture/framework/helpers/DefaultSenderTest.java +++ b/metafacture-framework/src/test/java/org/metafacture/framework/helpers/DefaultSenderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.framework.helpers; import static org.junit.Assert.assertFalse; diff --git a/metafacture-html/build.gradle b/metafacture-html/build.gradle index e8c8ab2f0..a076e3371 100644 --- a/metafacture-html/build.gradle +++ b/metafacture-html/build.gradle @@ -21,7 +21,7 @@ dependencies { api project(':metafacture-framework') implementation project(':metafacture-commons') implementation 'org.slf4j:slf4j-api:1.7.21' - implementation 'org.apache.commons:commons-compress:1.12' + implementation 'org.apache.commons:commons-compress:1.21' implementation 'commons-io:commons-io:2.6' implementation 'org.jsoup:jsoup:1.12.1' testImplementation 'junit:junit:4.12' diff --git a/metafacture-html/src/main/java/org/metafacture/html/ElementExtractor.java b/metafacture-html/src/main/java/org/metafacture/html/ElementExtractor.java index 19acdc868..5092378de 100644 --- a/metafacture-html/src/main/java/org/metafacture/html/ElementExtractor.java +++ b/metafacture-html/src/main/java/org/metafacture/html/ElementExtractor.java @@ -13,15 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.metafacture.html; -import java.io.IOException; -import java.io.Reader; +package org.metafacture.html; -import org.apache.commons.io.IOUtils; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; import org.metafacture.framework.FluxCommand; import org.metafacture.framework.ObjectReceiver; import org.metafacture.framework.annotations.Description; @@ -29,6 +23,14 @@ import org.metafacture.framework.annotations.Out; import org.metafacture.framework.helpers.DefaultObjectPipe; +import org.apache.commons.io.IOUtils; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; + +import java.io.IOException; +import java.io.Reader; + /** * Extracts the the specified element from an HTML document * @@ -51,10 +53,11 @@ public ElementExtractor(final String selector) { @Override public void process(final Reader reader) { try { - Document document = Jsoup.parse(IOUtils.toString(reader)); - Element firstElement = document.select(selector).first(); + final Document document = Jsoup.parse(IOUtils.toString(reader)); + final Element firstElement = document.select(selector).first(); getReceiver().process(firstElement.data()); - } catch (IOException e) { + } + catch (final IOException e) { e.printStackTrace(); } } diff --git a/metafacture-html/src/main/java/org/metafacture/html/HtmlDecoder.java b/metafacture-html/src/main/java/org/metafacture/html/HtmlDecoder.java index 0cee715b9..eeb1e4397 100644 --- a/metafacture-html/src/main/java/org/metafacture/html/HtmlDecoder.java +++ b/metafacture-html/src/main/java/org/metafacture/html/HtmlDecoder.java @@ -13,8 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.html; +import org.metafacture.framework.FluxCommand; +import org.metafacture.framework.StreamReceiver; +import org.metafacture.framework.annotations.Description; +import org.metafacture.framework.annotations.In; +import org.metafacture.framework.annotations.Out; +import org.metafacture.framework.helpers.DefaultObjectPipe; + +import org.apache.commons.io.IOUtils; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Attribute; +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.Reader; import java.io.UnsupportedEncodingException; @@ -24,43 +41,36 @@ import java.util.Map; import java.util.UUID; -import org.apache.commons.io.IOUtils; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Attribute; -import org.jsoup.nodes.Attributes; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.metafacture.framework.FluxCommand; -import org.metafacture.framework.StreamReceiver; -import org.metafacture.framework.annotations.Description; -import org.metafacture.framework.annotations.In; -import org.metafacture.framework.annotations.Out; -import org.metafacture.framework.helpers.DefaultObjectPipe; - /** * Decode HTML to metadata events. Each input document represents one record. - * + * * @author Fabian Steeg (fsteeg) * */ -@Description("Decode HTML to metadata events. The attrValsAsSubfields option can be used to override " - + "the default attribute values to be used as subfields (e.g. by default " - + "`link rel=\"canonical\" href=\"http://example.org\"` becomes `link.canonical`). " - + "It expects an HTTP-style query string specifying as key the attributes whose value should " - + "be used as a subfield, and as value the attribute whose value should be the subfield value, " - + "e.g. the default contains `link.rel=href`. To use the HTML element text as the value " - + "(instead of another attribute), omit the value of the query-string key-value pair, " - + "e.g. `title.lang`. To add to the defaults, instead of replacing them, start with an `&`, " - + "e.g. `&h3.class`") +@Description("Decode HTML to metadata events. The attrValsAsSubfields option can be used to override " + + "the default attribute values to be used as subfields (e.g. by default " + + "`link rel=\"canonical\" href=\"http://example.org\"` becomes `link.canonical`). " + + "It expects an HTTP-style query string specifying as key the attributes whose value should " + + "be used as a subfield, and as value the attribute whose value should be the subfield value, " + + "e.g. the default contains `link.rel=href`. To use the HTML element text as the value " + + "(instead of another attribute), omit the value of the query-string key-value pair, " + + "e.g. `title.lang`. To add to the defaults, instead of replacing them, start with an `&`, " + + "e.g. `&h3.class`") @In(Reader.class) @Out(StreamReceiver.class) @FluxCommand("decode-html") public class HtmlDecoder extends DefaultObjectPipe { + private static final Logger LOG = LoggerFactory.getLogger(HtmlDecoder.class); + private static final String DEFAULT_ATTR_VALS_AS_SUBFIELDS = // "meta.name=content&meta.property=content&link.rel=href&a.rel=href"; + private Map attrValsAsSubfields; + /** + * Creates an instance of {@link HtmlDecoder}. + */ public HtmlDecoder() { setAttrValsAsSubfields(DEFAULT_ATTR_VALS_AS_SUBFIELDS); } @@ -68,28 +78,29 @@ public HtmlDecoder() { @Override public void process(final Reader reader) { try { - StreamReceiver receiver = getReceiver(); + final StreamReceiver receiver = getReceiver(); receiver.startRecord(UUID.randomUUID().toString()); - Document document = Jsoup.parse(IOUtils.toString(reader)); + final Document document = Jsoup.parse(IOUtils.toString(reader)); process(document, receiver); receiver.endRecord(); - } catch (IOException e) { - e.printStackTrace(); + } + catch (final IOException e) { + LOG.error(e.getMessage(), e); } } - private void process(Element parent, StreamReceiver receiver) { - for (Element element : parent.children()) { + private void process(final Element parent, final StreamReceiver receiver) { + for (final Element element : parent.children()) { receiver.startEntity(element.nodeName()); - Attributes attributes = element.attributes(); + final Attributes attributes = element.attributes(); boolean addedValueAsSubfield = false; - for (Attribute attribute : attributes) { + for (final Attribute attribute : attributes) { addedValueAsSubfield = handleAttributeValuesAsSubfields(receiver, element, attributes, attribute); receiver.literal(attribute.getKey(), attribute.getValue()); } if (element.children().isEmpty()) { - String text = element.text().trim(); - String value = text.isEmpty() ? element.data() : text; + final String text = element.text().trim(); + final String value = text.isEmpty() ? element.data() : text; if (!value.isEmpty() && !addedValueAsSubfield) { receiver.literal("value", value); } @@ -99,35 +110,41 @@ private void process(Element parent, StreamReceiver receiver) { } } - private boolean handleAttributeValuesAsSubfields(StreamReceiver receiver, Element element, - Attributes attributes, Attribute attribute) { - String fullFieldKey = element.nodeName() + "." + attribute.getKey(); + private boolean handleAttributeValuesAsSubfields(final StreamReceiver receiver, final Element element, final Attributes attributes, final Attribute attribute) { + final String fullFieldKey = element.nodeName() + "." + attribute.getKey(); if (attrValsAsSubfields.containsKey(fullFieldKey)) { - String configValue = attrValsAsSubfields.get(fullFieldKey); + final String configValue = attrValsAsSubfields.get(fullFieldKey); if (configValue.trim().isEmpty()) { receiver.literal(attribute.getValue(), element.text().trim()); return true; - } else { - String value = attributes.get(configValue); + } + else { + final String value = attributes.get(configValue); receiver.literal(attribute.getValue(), value); } } return false; } - public void setAttrValsAsSubfields(String mapString) { + /** + * Sets attribute values as subfields. If the value(s) start with an `&` they + * are appended to {@link #DEFAULT_ATTR_VALS_AS_SUBFIELDS}. + * + * @param mapString the attributes to be added as subfields + */ + public void setAttrValsAsSubfields(final String mapString) { this.attrValsAsSubfields = new HashMap(); - String input = mapString.startsWith("&") ? DEFAULT_ATTR_VALS_AS_SUBFIELDS + mapString - : mapString; - for (String nameValuePair : input.split("&")) { - String[] nameValue = nameValuePair.split("="); + final String input = mapString.startsWith("&") ? DEFAULT_ATTR_VALS_AS_SUBFIELDS + mapString : mapString; + for (final String nameValuePair : input.split("&")) { + final String[] nameValue = nameValuePair.split("="); try { - String utf8 = StandardCharsets.UTF_8.name(); - String key = URLDecoder.decode(nameValue[0], utf8); - String val = nameValue.length > 1 ? URLDecoder.decode(nameValue[1], utf8) : ""; + final String utf8 = StandardCharsets.UTF_8.name(); + final String key = URLDecoder.decode(nameValue[0], utf8); + final String val = nameValue.length > 1 ? URLDecoder.decode(nameValue[1], utf8) : ""; attrValsAsSubfields.put(key, val); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); + } + catch (final UnsupportedEncodingException e) { + LOG.error(e.getMessage(), e); } } } diff --git a/metafacture-html/src/test/java/org/metafacture/html/ElementExtractorTest.java b/metafacture-html/src/test/java/org/metafacture/html/ElementExtractorTest.java index 860af9f34..b8fb665c4 100644 --- a/metafacture-html/src/test/java/org/metafacture/html/ElementExtractorTest.java +++ b/metafacture-html/src/test/java/org/metafacture/html/ElementExtractorTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.metafacture.html; import static org.mockito.Mockito.verify; @@ -38,7 +39,7 @@ public final class ElementExtractorTest { private static final StringReader IN = new StringReader("" + "" + "