de.cosmocode.rendering.CollectionRenderer.java Source code

Java tutorial

Introduction

Here is the source code for de.cosmocode.rendering.CollectionRenderer.java

Source

/**
 * Copyright 2010 CosmoCode GmbH
 *
 * 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 de.cosmocode.rendering;

import java.util.Deque;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.map.LinkedMap;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

/**
 * An implementation of the {@link Renderer} interface which builds a 
 * structure of {@link List}s and {@link Map}s.
 *
 * @author Willi Schoenborn
 */
public final class CollectionRenderer extends AbstractRenderer {

    private final Deque<Object> stack = Lists.newLinkedList();

    private Mode mode = Mode.INITIAL;

    private Object build;

    private List<Object> peekList() {
        if (mode == Mode.LIST) {
            final Object peek = stack.element();
            @SuppressWarnings("unchecked")
            final List<Object> list = List.class.cast(peek);
            return list;
        } else {
            throw new RenderingException(String.format("Expected mode to be %s, but was %s", Mode.LIST, mode));
        }
    }

    private boolean peekIsList() {
        return stack.peek() instanceof List<?>;
    }

    private LinkedMap peekMap() {
        if (mode == Mode.MAP || mode == Mode.KEY) {
            final Object peek = stack.element();
            assert peek instanceof LinkedMap;
            return LinkedMap.class.cast(peek);
        } else {
            throw new RenderingException(String.format("Expected mode to be %s, but was %s", Mode.MAP, mode));
        }
    }

    private boolean peekIsMap() {
        return stack.peek() instanceof Map<?, ?>;
    }

    private Renderer append(Object value) {
        if (mode == Mode.LIST) {
            peekList().add(value);
        } else if (mode == Mode.KEY) {
            final LinkedMap map = peekMap();
            map.put(map.lastKey(), value);
            mode = Mode.MAP;
        } else {
            throw new RenderingException(String.format("Appending only works in %s and %s", Mode.LIST, Mode.KEY));
        }
        return this;
    }

    @Override
    public Renderer list() throws RenderingException {
        mode.checkAllowed(Mode.LIST);
        stack.push(Lists.newLinkedList());
        mode = Mode.LIST;
        return this;
    }

    @Override
    public Renderer endList() throws RenderingException {
        if (mode == Mode.LIST) {
            final List<Object> peek = peekList();
            stack.pop();
            if (stack.isEmpty()) {
                mode = Mode.DONE;
                build = peek;
            } else if (peekIsList()) {
                mode = Mode.LIST;
                peekList().add(peek);
            } else if (peekIsMap()) {
                mode = Mode.MAP;
                final LinkedMap map = peekMap();
                map.put(map.lastKey(), peek);
            } else {
                throw new RenderingException("Unknown state");
            }
            return this;
        } else {
            throw new RenderingException(String.format("endList is not allowed when in %s mode", mode));
        }
    }

    @Override
    public Renderer map() throws RenderingException {
        mode.checkAllowed(Mode.MAP);
        stack.push(new LinkedMap());
        mode = Mode.MAP;
        return this;
    }

    @Override
    public Renderer endMap() throws RenderingException {
        if (mode == Mode.MAP) {
            final LinkedMap peek = peekMap();
            stack.pop();
            if (stack.isEmpty()) {
                mode = Mode.DONE;
                build = peek;
            } else if (peekIsList()) {
                mode = Mode.LIST;
                peekList().add(peek);
            } else if (peekIsMap()) {
                mode = Mode.MAP;
                final LinkedMap map = peekMap();
                map.put(map.lastKey(), peek);
            } else {
                throw new RenderingException("Unknown state");
            }
            return this;
        } else {
            throw new RenderingException(String.format("endMap is not allowed when in %s mode", mode));
        }
    }

    @Override
    public Renderer key(CharSequence key) throws RenderingException {
        mode.checkAllowed(Mode.KEY);
        peekMap().put(key == null ? null : key.toString(), null);
        mode = Mode.KEY;
        return this;
    }

    @Override
    public Renderer nullValue() throws RenderingException {
        return append(null);
    }

    @Override
    public Renderer value(boolean value) throws RenderingException {
        return append(Boolean.valueOf(value));
    }

    @Override
    public Renderer value(CharSequence value) throws RenderingException {
        return value == null ? nullValue() : append(value);
    }

    /**
     * {@inheritDoc}
     * <strong>Note</strong>: Beware of auto-boxing.
     */
    @Override
    public Renderer value(double value) throws RenderingException {
        return append(value);
    }

    /**
     * {@inheritDoc}
     * <strong>Note</strong>: Beware of auto-boxing.
     */
    @Override
    public Renderer value(long value) throws RenderingException {
        return append(value);
    }

    @Override
    public Object build() throws RenderingException {
        if (mode == Mode.DONE) {
            assert stack.isEmpty();
            Preconditions.checkState(build != null, "Structure not finished yet");
            return build;
        } else {
            throw new RenderingException(String.format("Structure not finished, current mode is %s", mode));
        }
    }

}