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.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.List;
023
024/**
025 * Entity collection with the following approach:  a size (number of children)
026 * and a prototypical child are assigned.  The prototype child is cloned
027 * <code>size</code> times, and these clones become the indexed child entities.
028 */
029public class EntityArray extends EntityCollectionSupport implements
030        IndexedEntityCollection {
031    private static final long serialVersionUID = 8716199462287161060L;
032
033    private int size = -1;
034    private int minimumSize = 0;
035    private int maximumSize = Integer.MAX_VALUE;
036    private boolean resizable = false;
037    private List<Entity> children;
038    private Entity prototype;
039
040    /**
041     * Create a new EntityArray.
042     */
043    public EntityArray() {
044        // default constructor
045    }
046
047    /**
048     * Create a new IndexedEntityCollection.
049     * @param size number of occurrences of the prototypical Entity
050     */
051    public EntityArray(int size) {
052        setSize(size);
053    }
054
055    /**
056     * Create a new EntityArray with the specified prototype.
057     * @param prototype Entity
058     */
059    public EntityArray(Entity prototype) {
060        setPrototype(prototype);
061    }
062
063    /**
064     * Create a new IndexedEntityCollection.
065     * @param prototype Entity
066     * @param size number of occurrences
067     */
068    public EntityArray(Entity prototype, int size) {
069        this(prototype);
070        setSize(size);
071    }
072
073    /**
074     * {@inheritDoc}
075     */
076    public Entity getChild(int index) {
077        initialize();
078        return (Entity) children.get(index);
079    }
080
081    /**
082     * {@inheritDoc}
083     */
084    public synchronized Collection<Entity> getChildren() {
085        initialize();
086        return Collections.unmodifiableCollection(children);
087    }
088
089    /**
090     * Get the prototype.
091     * @return Entity.
092     */
093    public synchronized Entity getPrototype() {
094        return prototype;
095    }
096
097    /**
098     * Set the prototype.
099     * @param prototype The Entity prototype to set.
100     */
101    public synchronized void setPrototype(Entity prototype) {
102        if (prototype == null) {
103            throw new IllegalArgumentException("prototype Entity was null");
104        }
105        this.prototype = prototype;
106    }
107
108    /**
109     * {@inheritDoc}
110     * @throws IllegalArgumentException if <code>size</code> is not valid.
111     */
112    public synchronized void setSize(int size) {
113        if (this.size >= 0 && !isResizable()) {
114            if (size == this.size()) {
115                return;
116            }
117            throw new IllegalStateException("size already set");
118        }
119        if (size < minimumSize || size > maximumSize) {
120            throw new IllegalArgumentException("illegal collection size: "
121                    + size + "; should be <= " + maximumSize + " and >= "
122                    + minimumSize);
123        }
124        this.size = size;
125    }
126
127    /**
128     * {@inheritDoc}
129     */
130    public synchronized int size() {
131        return size;
132    }
133
134    /**
135     * {@inheritDoc}
136     */
137    public synchronized boolean isSizable() {
138        return size == -1 || isResizable();
139    }
140
141    /**
142     * Clone this EntityArray.
143     * @return new EntityArray
144     */
145    @Override
146    public synchronized EntityArray clone() {
147        EntityArray result = (EntityArray) super.clone();
148        if (children != null) {
149            result.children = new ArrayList<Entity>();
150            for (Entity e : children) {
151                result.children.add(e.clone());
152            }
153        }
154        return result;
155    }
156
157    /**
158     * Initialize this EntityArray.
159     */
160    private synchronized void initialize() {
161        if (children != null) {
162            return;
163        }
164        if (size < 0) {
165            throw new IllegalStateException("EntityArray size not set");
166        }
167        if (size == 0) {
168            children = Collections.<Entity>emptyList();
169            return;
170        }
171        if (prototype == null) {
172            throw new IllegalStateException("Prototype child entity not set");
173        }
174        children = new ArrayList<Entity>(size);
175        adjustSize();
176    }
177
178    /**
179     * Adjust the size of this EntityArray.
180     */
181    private synchronized void adjustSize() {
182        if (children.size() > size) {
183            children.subList(size, children.size()).clear();
184            return;
185        }
186        for (int i = children.size(); i < size; i++) {
187            children.add(prototype.clone());
188        }
189    }
190
191    /**
192     * Get the int maximumSize.
193     * @return int
194     */
195    public synchronized int getMaximumSize() {
196        return maximumSize;
197    }
198
199    /**
200     * Set the int maximumSize.
201     * @param maximumSize int
202     */
203    public synchronized void setMaximumSize(int maximumSize) {
204        if (maximumSize > Integer.MAX_VALUE || maximumSize < getMinimumSize()) {
205            throw new IllegalArgumentException(Integer.toString(maximumSize));
206        }
207        this.maximumSize = maximumSize;
208    }
209
210    /**
211     * Get the int minimumSize.
212     * @return int
213     */
214    public synchronized int getMinimumSize() {
215        return minimumSize;
216    }
217
218    /**
219     * Set the int minimumSize.
220     * @param minimumSize int
221     */
222    public synchronized void setMinimumSize(int minimumSize) {
223        if (minimumSize < 0 || minimumSize > getMaximumSize()) {
224            throw new IllegalArgumentException(Integer.toString(minimumSize));
225        }
226        this.minimumSize = minimumSize;
227    }
228
229    /**
230     * Get the boolean resizable.
231     * @return boolean
232     */
233    public boolean isResizable() {
234        return resizable;
235    }
236
237    /**
238     * Set the boolean resizable.
239     * @param resizable boolean
240     */
241    public void setResizable(boolean resizable) {
242        this.resizable = resizable;
243    }
244
245}