001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.flatfile;
018
019import java.io.Serializable;
020import java.util.AbstractList;
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.Iterator;
025import java.util.LinkedHashMap;
026import java.util.List;
027import java.util.Map;
028
029import org.apache.commons.collections4.IteratorUtils;
030import org.apache.commons.collections4.Transformer;
031import org.apache.commons.lang3.ArrayUtils;
032import org.apache.commons.lang3.Validate;
033
034/**
035 * Basic implementation of NamedEntityCollection.
036 * @version $Revision: 1766123 $ $Date: 2016-10-21 15:40:45 -0500 (Fri, 21 Oct 2016) $
037 */
038public class EntityMap extends EntityCollectionSupport implements NamedEntityCollection {
039
040    private static final long serialVersionUID = 3162898927743996952L;
041
042    private static class Child implements Serializable {
043
044        /** Serialization version */
045        private static final long serialVersionUID = 2347729609002856564L;
046
047        private final String name;
048        @SuppressWarnings("PMD.UnusedPrivateField") // false positive; this field is used in EntityMap#clone()
049        private final Entity entity;
050
051        /**
052         * Create a new Child instance.
053         * @param name of child
054         * @param entity child
055         */
056        public Child(String name, Entity entity) {
057            this.name = name;
058            if (entity == null) {
059                throw new IllegalArgumentException();
060            }
061            this.entity = entity;
062        }
063
064    }
065
066    /**
067     * Expose the Child list as List<Entity>. This is necessary because we permit nameless children,
068     * which we want to account for in a layout but have no need of referencing later.
069     */
070    private static class ChildrenList extends AbstractList<Entity> implements Serializable {
071
072        private static final long serialVersionUID = -954784669450571284L;
073
074        private static final Transformer<Child, Entity> CHILD_TO_ENTITY = new Transformer<Child, Entity>() {
075
076            public Entity transform(Child input) {
077                return input.entity;
078            }
079        };
080
081        private final List<Child> wrapped;
082
083        /**
084         * Create a new {@link ChildrenList} wrapping the specified {@link List}.
085         * @param wrapped {@link List}
086         */
087        private ChildrenList(List<Child> wrapped) {
088            super();
089            this.wrapped = wrapped;
090        }
091
092        /**
093         * {@inheritDoc}
094         */
095        @Override
096        public Entity get(int index) {
097            return CHILD_TO_ENTITY.transform(wrapped.get(index));
098        }
099
100        /**
101         * {@inheritDoc}
102         */
103        @Override
104        public Iterator<Entity> iterator() {
105            return IteratorUtils.transformedIterator(wrapped.iterator(), CHILD_TO_ENTITY);
106        }
107
108        @Override
109        public int size() {
110            return wrapped.size();
111        }
112
113    }
114
115    /** All children */
116    private List<Child> children;
117
118    /** contains mapped children only */
119    private Map<String, Entity> childMap;
120
121    private List<Entity> exposeChildrenList;
122
123    /**
124     * Add a child.
125     * @param name if {@code null} filler element
126     * @param child non-null child Entity
127     */
128    public synchronized void add(String name, Entity child) {
129        Validate.notNull(child, "child entity is null");
130
131        if (children == null) {
132            children = new ArrayList<Child>();
133            exposeChildrenList = new ChildrenList(children);
134            childMap = new LinkedHashMap<String, Entity>();
135        }
136        if (childMap.containsKey(name)) {
137            throw new IllegalArgumentException("cannot add > 1 child entity '" + name + "'");
138        }
139        if (name != null) {
140            childMap.put(name, child);
141        }
142        children.add(new Child(name, child));
143    }
144
145    /**
146     * Get a map of the children.
147     * @return Map<String, Entity>
148     */
149    public synchronized Map<String, Entity> getChildMap() {
150        return childMap == null ? Collections.<String, Entity> emptyMap() : Collections.unmodifiableMap(childMap);
151    }
152
153    /**
154     * {@inheritDoc}
155     */
156    public synchronized String[] getChildNames() {
157        return childMap == null ? ArrayUtils.EMPTY_STRING_ARRAY
158            : childMap.keySet().toArray(new String[childMap.size()]);
159    }
160
161    /**
162     * {@inheritDoc}
163     */
164    public Entity getChild(String name) {
165        return getChildMap().get(name);
166    }
167
168    /**
169     * {@inheritDoc}
170     */
171    public boolean hasChild(String name) {
172        return getChildMap().containsKey(name);
173    }
174
175    /**
176     * {@inheritDoc}
177     */
178    public synchronized Collection<Entity> getChildren() {
179        return children == null ? Collections.<Entity> emptyList() : Collections.unmodifiableList(exposeChildrenList);
180    }
181
182    /**
183     * {@inheritDoc}
184     */
185    @Override
186    public synchronized EntityMap clone() {
187        EntityMap result = (EntityMap) super.clone();
188        result.children = null;
189        result.childMap = null;
190        result.exposeChildrenList = null;
191        if (children != null) {
192            for (Child child : children) {
193                result.add(child.name, child.entity.clone());
194            }
195        }
196        return result;
197    }
198}