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.ByteArrayInputStream;
020import java.io.ByteArrayOutputStream;
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.OutputStream;
024import java.util.Iterator;
025
026import org.apache.commons.lang3.ArrayUtils;
027
028/**
029 * Implementation of common operations for an EntityCollection.
030 * @version $Revision: 1301248 $ $Date: 2012-03-15 17:20:49 -0500 (Thu, 15 Mar 2012) $
031 */
032public abstract class EntityCollectionSupport extends EntitySupport implements EntityCollection {
033    /** Serialization version */
034    private static final long serialVersionUID = 5902476686737324304L;
035
036    private static final byte[] DEFAULT_DELIMITER = ArrayUtils.EMPTY_BYTE_ARRAY;
037
038    private byte[] delim = DEFAULT_DELIMITER;
039    private boolean delimAfter = true;
040    private boolean suppressEmptyChildren = true;
041
042    /**
043     * {@inheritDoc}
044     */
045    public final synchronized int length() {
046        int result = 0;
047        for (Iterator<Entity> it = getChildren().iterator(); it.hasNext();) {
048            Entity e = it.next();
049            if (shouldSuppress(e)) {
050                continue;
051            }
052            result += e.length();
053            if (it.hasNext() || isDelimAfter()) {
054                result += getDelim().length;
055            }
056        }
057        return result;
058    }
059
060    /**
061     * {@inheritDoc}
062     */
063    public final synchronized void readFrom(InputStream is) throws IOException {
064        for (Iterator<Entity> it = getChildren().iterator(); it.hasNext();) {
065            Entity e = it.next();
066            if (shouldSuppress(e)) {
067                continue;
068            }
069            e.readFrom(is);
070            if (it.hasNext() || isDelimAfter()) {
071                is.skip(getDelim().length);
072            }
073        }
074    }
075
076    /**
077     * {@inheritDoc}
078     */
079    public final synchronized void writeTo(OutputStream os) throws IOException {
080        for (Iterator<Entity> it = getChildren().iterator(); it.hasNext();) {
081            Entity e = it.next();
082            if (shouldSuppress(e)) {
083                continue;
084            }
085            e.writeTo(os);
086            if (it.hasNext() || isDelimAfter()) {
087                os.write(getDelim());
088            }
089        }
090    }
091
092    /**
093     * {@inheritDoc}
094     */
095    public final synchronized byte[] getValue() {
096        ByteArrayOutputStream baos = new ByteArrayOutputStream();
097        try {
098            writeTo(baos);
099        } catch (IOException e) {
100            throw new RuntimeException(e);
101        }
102        return baos.toByteArray();
103    }
104
105    /**
106     * {@inheritDoc}
107     */
108    public final synchronized void setValue(byte[] b) {
109        try {
110            readFrom(new ByteArrayInputStream(b));
111        } catch (IOException e) {
112            throw new RuntimeException(e);
113        }
114    }
115
116    /**
117     * {@inheritDoc}
118     */
119    public final void setValue(byte[] b, int offset, int length) {
120        try {
121            readFrom(new ByteArrayInputStream(b, offset, length));
122        } catch (IOException e) {
123            throw new RuntimeException(e);
124        }
125    }
126
127    /**
128     * Learn whether a trailing delimiter will be added when a delimiter is being used.
129     * @return boolean.
130     */
131    public boolean isDelimAfter() {
132        return delimAfter;
133    }
134
135    /**
136     * Set whether to add a trailing delimiter when a delimiter is being used.
137     * Default is <code>true</code>.
138     * @param delimAfter The boolean delimAfter to set.
139     */
140    public void setDelimAfter(boolean delimAfter) {
141        this.delimAfter = delimAfter;
142    }
143
144    /**
145     * Get the delimiter to use between children.
146     * @return byte[].
147     */
148    public byte[] getDelim() {
149        return delim;
150    }
151
152    /**
153     * Set the delimiter to use between children.
154     * @param delim The byte[] delimiter to set.
155     */
156    public synchronized void setDelim(byte[] delim) {
157        this.delim = delim == null ? DEFAULT_DELIMITER : delim;
158    }
159
160    /**
161     * Get the boolean suppressEmptyChildren.
162     * @return boolean
163     */
164    public boolean isSuppressEmptyChildren() {
165        return suppressEmptyChildren;
166    }
167
168    /**
169     * Set the boolean suppressEmptyChildren.
170     * @param suppressEmptyChildren boolean
171     */
172    public void setSuppressEmptyChildren(boolean suppressEmptyChildren) {
173        this.suppressEmptyChildren = suppressEmptyChildren;
174    }
175
176    /**
177     * {@inheritDoc}
178     */
179    @Override
180    public EntityCollectionSupport clone() {
181        return (EntityCollectionSupport) super.clone();
182    }
183
184    /**
185     * Learn whether the specified child should be suppressed.
186     * @param child to check
187     * @return boolean
188     */
189    protected boolean shouldSuppress(Entity child) {
190        return isSuppressEmptyChildren() && child.length() == 0;
191    }
192
193}