View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.trinidad.component;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.List;
24  
25  import java.util.Map;
26  
27  import javax.faces.component.UIComponent;
28  
29  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
30  
31  
32  /**
33   * List for storing children.
34   *
35   */
36  class ChildArrayList extends ArrayList<UIComponent>
37  {
38    public ChildArrayList(UIComponent parent)
39    {
40      _parent = parent;
41    }
42  
43    @Override
44    public void add(int index, UIComponent element)
45    {
46      if (element == null)
47        throw new NullPointerException();
48  
49      if ((index < 0) || (index > size()))
50        throw new IndexOutOfBoundsException(_LOG.getMessage(
51          "INDEX_SIZE", new Object[]{index, size()}));
52  
53      UIComponent oldParent = element.getParent();
54      if (oldParent != null)
55      {
56        int adjustedIndex = __removeFromParent(element, index);
57        // Only adjust the index when the child is re-added to the same parent
58        if (oldParent == _parent)
59        {
60          index = adjustedIndex; 
61        }
62      }
63      
64      // do not change the order of these calls, see TRINIDAD-1674 for more info
65      super.add(index, element);
66      element.setParent(_parent);
67    }
68  
69    
70    @Override
71    public boolean add(UIComponent element)
72    {
73      add(size(), element);
74      return true;
75    }
76    
77    @Override
78    public boolean addAll(Collection<? extends UIComponent> collection)
79    {
80      return addAll(size(), collection);
81    }
82  
83    @Override
84    public boolean addAll(
85        int index, 
86        Collection<? extends UIComponent> collection)
87    {
88      boolean changed = false;
89      for(UIComponent element : collection)
90      {
91        if (element == null)
92          throw new NullPointerException();
93  
94        add(index++, element);
95        changed = true;
96      }
97      
98      return changed;
99    }
100 
101   @Override
102   public UIComponent remove(int index)
103   {
104     UIComponent child = super.remove(index);
105     child.setParent(null);
106 
107     return child;
108   }
109 
110   @Override
111   public boolean remove(Object element)
112   {
113     if (element == null)
114       throw new NullPointerException();
115     
116     if (!(element instanceof UIComponent))
117       return false;
118   
119     if (super.remove(element))
120     {
121       UIComponent child = (UIComponent) element;
122       child.setParent(null);
123       return true;
124     }
125 
126     return false;
127   }
128 
129   @Override
130   public void clear()
131   {
132     int size = this.size();
133     
134     while ( size > 0)
135     {
136       size--;
137       remove(size);
138     }
139     
140     super.clear();
141   }
142   
143   @Override
144   public boolean removeAll(Collection<?> collection)
145   {
146     boolean result = false;
147     for (Object element : collection)
148     {
149       if (remove(element))
150         result = true;
151     }
152     
153     return result;
154   }
155 
156   @Override
157   public UIComponent set(int index, UIComponent element)
158   {
159     if (element == null)
160       throw new NullPointerException();
161     
162     if ((index < 0) || (index >= size()))
163       throw new IndexOutOfBoundsException();
164 
165     UIComponent child = element;
166     UIComponent previous = get(index);
167 
168     previous.setParent(null);
169     
170     child.setParent(_parent);
171     super.set(index, element);
172     
173     return previous;
174   }
175 
176   @SuppressWarnings("unchecked")
177   static int __removeFromParent(
178     UIComponent component,
179     int index)
180   {
181     UIComponent parent = component.getParent();
182     assert(parent != null);
183 
184     if (parent.getChildCount() > 0)
185     {
186       List<UIComponent> children = parent.getChildren();
187       int size = children.size();
188       for  (int i = 0; i < size; i++)
189       {
190         if  (children.get(i) == component)
191         {
192           children.remove(i);
193           if (index > i)
194             index--;
195           return index;
196         }
197       }
198     }
199     
200     // TRINIDAD-2369: Until TRINIDAD-2368 is fixed, we have to call remove() on the map
201     // returned by getFacets() rather than calling remove() on getFacets().values()
202     // Note that the old code used to call getFacets().values().contains(), which would iterate on the entry set.
203     // The new code performs the same iteration, so it should not be any slower.
204     
205     Map<String, UIComponent> facets = parent.getFacets();
206     for (Map.Entry<String, UIComponent> entry: facets.entrySet())
207     {
208       if (entry.getValue() == component)
209       {
210         facets.remove(entry.getKey());
211         return index;
212       }
213     }
214 
215     // Not good - the child thought it was in a parent,
216     // but it wasn't.
217     assert(false);
218     return index;
219   }
220 
221   private final UIComponent _parent;
222   private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(
223     ChildArrayList.class);
224   private static final long serialVersionUID = 1L;
225 
226 }