001    /*
002     * Copyright 2012 Mark Slater
003     *
004     * 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
005     *
006     *      http://www.apache.org/licenses/LICENSE-2.0
007     *
008     * 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.
009     */
010    
011    package argo.jdom;
012    
013    /**
014     * Instances of {@code JsonNodeSelector} extract values from {@code Object}s of a specified type.
015     * <p/>
016     * <p>For example, given a {@code JsonNode} representing {@code {"Fee":{"fi":"fo"}}},
017     * <pre>
018     * anObjectNodeWithField("Fee")
019     * .withChild(anObjectNodeWithField("fi"))
020     * .withChild(aStringNode())
021     * .getValue(jsonNode)
022     * </pre> will return the {@code String} "fo".</p>
023     *
024     * @param <T> The type of Object worked on.
025     * @param <U> The type of Object returned.
026     */
027    public final class JsonNodeSelector<T, U> {
028    
029        private final Functor<T, U> valueGetter;
030    
031        JsonNodeSelector(final Functor<T, U> valueGetter) {
032            this.valueGetter = valueGetter;
033        }
034    
035        /**
036         * Determines whether this {@code JsonNodeSelector} can extract a value from the given {@code JsonNode}.
037         *
038         * @param jsonNode the {@code JsonNode} to test.
039         * @return true if a value can be extracted from the given {@code JsonNode}, false otherwise.
040         */
041        public boolean matches(final T jsonNode) {
042            return valueGetter.matchesNode(jsonNode);
043        }
044    
045        /**
046         * Extracts a value from the give {@code JsonNode}.
047         *
048         * @param argument the {@code JsonNode} to extract a value from.
049         * @return the extracted value.
050         * @throws IllegalArgumentException if calling {@code matches{@code  with the given {@code JsonNode} would return false, indicating no value can be extracted from it.
051         */
052        public U getValue(final T argument) {
053            return valueGetter.applyTo(argument);
054        }
055    
056        /**
057         * <p>Constructs a JsonNodeSelector consisting of this chained with the given {@code JsonNodeSelector}.</p>
058         * <p/>
059         * <p>For example, if we have {@code JsonNodeSelectors} for the first element of an array, and another that
060         * selects the second element of an array, and we chain them together in that order, we will get a selector that
061         * works on nested arrays, selecting the second element from an array stored in the first element of a parent
062         * array.</p>
063         *
064         * @param childJsonNodeSelector the {@code JsonNodeSelector} to chain onto this.
065         * @param <V>                   the type the chained {@code JsonNodeSelector} will return.
066         * @return a new {@code JsonNodeSelector} representing this, and the given selector applied in sequence.
067         */
068        public <V> JsonNodeSelector<T, V> with(final JsonNodeSelector<U, V> childJsonNodeSelector) {
069            return new JsonNodeSelector<T, V>(new ChainedFunctor<T, U, V>(this, childJsonNodeSelector));
070        }
071    
072        String shortForm() {
073            return valueGetter.shortForm();
074        }
075    
076        @Override
077        public String toString() {
078            return valueGetter.toString();
079        }
080    
081    }