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}