/* * Copyright 2016 Christoph Böhme * * Licensed under the Apache License, Version 2.0 the "License"; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.culturegraph.mf.test.validators; import java.util.function.Consumer; import org.culturegraph.mf.framework.StreamReceiver; /** * Checks that the received stream events are in the correct order and contain * valid data. If not, a user-supplied error handler method is invoked. The * error handler can be set via {@link #setErrorHandler(Consumer)}. The * {@link #DEFAULT_ERROR_HANDLER} does nothing. * <p> * Errors caused by an invalid order of the event stream have no effect on the * state of {@code WellformednessChecker}. The module behaves as if the * invalid event was never received. * <p> * Errors caused by invalid data are reported but otherwise handled as valid * events. This means that <i>start-record</i> or <i>start-entity</i> events * still start a record or entity even if their identifier or name was reported * as invalid. * * @author Christoph Böhme * */ public final class WellformednessChecker implements StreamReceiver { /** * The default error handler which is used if no other error handler was set * via {@link #setErrorHandler(Consumer)}. It does nothing. */ public static final Consumer<String> DEFAULT_ERROR_HANDLER = msg -> { /* do nothing */ }; private static final String ID_MUST_NOT_BE_NULL = "id must not be null"; private static final String NAME_MUST_NOT_BE_NULL = "name must not be null"; private static final String NOT_IN_RECORD = "Not in record"; private static final String NOT_IN_ENTITY = "Not in entity"; private static final String IN_ENTITY = "In entity"; private static final String IN_RECORD = "In record"; private Consumer<String> errorHandler = DEFAULT_ERROR_HANDLER; private int nestingLevel; /** * Sets the error handler which is called when a invalid event stream is * received. * <p> * The handler is called with a message describing the error. * * @param errorHandler a method which receives an error message */ public void setErrorHandler(final Consumer<String> errorHandler) { this.errorHandler = errorHandler; } public Consumer<String> getErrorHandler() { return errorHandler; } @Override public void startRecord(final String identifier) { if (nestingLevel > 0) { errorHandler.accept(IN_RECORD); } else { nestingLevel += 1; } if (identifier == null) { errorHandler.accept(ID_MUST_NOT_BE_NULL); } } @Override public void endRecord() { if (nestingLevel < 1) { errorHandler.accept(NOT_IN_RECORD); } else if (nestingLevel > 1) { errorHandler.accept(IN_ENTITY); } else { nestingLevel -= 1; } } @Override public void startEntity(final String name) { if (nestingLevel < 1) { errorHandler.accept(NOT_IN_RECORD); } else { nestingLevel += 1; } if (name == null) { errorHandler.accept(NAME_MUST_NOT_BE_NULL); } } @Override public void endEntity() { if (nestingLevel < 2) { errorHandler.accept(NOT_IN_ENTITY); } else { nestingLevel -= 1; } } @Override public void literal(final String name, final String value) { if (nestingLevel < 1) { errorHandler.accept(NOT_IN_RECORD); } if (name == null) { errorHandler.accept(NAME_MUST_NOT_BE_NULL); } } @Override public void resetStream() { nestingLevel = 0; } @Override public void closeStream() { if (nestingLevel > 0) { errorHandler.accept(IN_RECORD); } } }