Container.java

// <editor-fold defaultstate="collapsed" desc="license">
/*
 * Copyright (c) 2014, Karl H. Beckers
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * * Neither the name of the <ORGANIZATION> nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 **/
// </editor-fold>
package net.jarre_de_the.griffin.types.data;

import java.util.List;
import net.jarre_de_the.griffin.exception.NoFieldFoundException;
import net.jarre_de_the.griffin.types.field.AbstractField;

/**
 * Interface for all data types which can contain fields.
 *
 * @author charly4711
 */
public interface Container {

    /**
     * Searches fields contained in this Container. This search recurses down
     * into nested Containers.
     *
     * @param <T>
     * @Container} for a field of the give name (label) and/or field type. This
     * method recurses down other {
     * @Container} objects contained in this one. Either parameter can be null
     * to always match. The method must not return null but an empty list, if no
     * match was found.
     *
     * @param clazz Class of field type to look for or null for any.
     * @param label Name of the field to find or null for any.
     * @return The list of (potentially multiple) fields found wrapped in
     * {@FoundField} objects that also contain the path to the field from
     * the Container that was searched on.
     */
    <T extends AbstractField> List<FoundField<T>> findField(Class<T> clazz,
                                                            byte[] label);

    /**
     * Behaves like @see #findField(Class<T>, byte[]) except that it also
     * compares the values of the fields to the value specified. Only an exact
     * match will count as a match.
     *
     * @param <T>
     * @param <M>
     * @param clazz
     * @param label
     * @param value
     * @return
     */
    <T extends AbstractField, M extends AbstractData> List<FoundField<T>>
            findField(Class<T> clazz, byte[] label, M value);

    /**
     * Behaves like @see #findField(Class<T>, byte[]) except that it also
     * uses toString() on the values of the fields and matches them to the
     * specifiec regular expression.
     *
     * @param <T>
     * @param clazz
     * @param label
     * @param regex
     * @return
     */
    <T extends AbstractField> List<FoundField<T>>
            findField(Class<T> clazz, byte[] label, String regex);

    /**
     * Searches fields contained directly in this {@Container} for a field
     * of the give label and field type. This method will not recurse and
     * therefore just find a single field.
     *
     * @param <T>
     * @throws NoFieldFoundException if no match was found.
     * @param clazz Class of field type to look for or null for any.
     * @param label Name of the field to find. Passing in null here is a sure
     * way of getting a NoFieldFoundException.
     * @return The hopefully unique field found.
     */
    <T extends AbstractField> T findLocalField(Class<T> clazz, byte[] label);

    /**
     * Searches a field by an absolutely specified position in the tree.
     *
     * @throws NoFieldFoundException if the path doesn't identify an existing
     * field.
     * @param path Array of integer positions at each level below the container.
     * @return The field found.
     */
    AbstractField findFieldByPath(int[] path);

    /**
     * Wrapper for results of Container.findField() methods.
     *
     * @param <T>
     */
    public static class FoundField<T extends AbstractField> {

        private final T field;
        private final int[] path;

        public FoundField(T field, int[] path) {
            this.field = field;
            if (null != path) {
                this.path = new int[path.length];
                System.arraycopy(path, 0, this.path, 0, path.length);
            } else {
                this.path = null;
            }
        }

        public T getField() {
            return field;
        }

        public int[] getPath() {
            int[] result;
            if (null != path) {
                result = new int[path.length];
                System.arraycopy(path, 0, result, 0, path.length);
            } else {
                result = null;
            }
            return result;
        }
    }
}