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}