1/*
2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package com.sun.org.apache.xerces.internal.util;
22
23import com.sun.org.apache.xerces.internal.xni.Augmentations;
24import java.util.Collections;
25import java.util.Enumeration;
26import java.util.HashMap;
27import java.util.Iterator;
28import java.util.Map;
29
30/**
31 * This class provides an implementation for Augmentations interface.
32 * Augmentations interface defines a map of additional data that could
33 * be passed along the document pipeline. The information can contain extra
34 * arguments or infoset augmentations, for example PSVI. This additional
35 * information is identified by a String key.
36 * <p>
37 *
38 * @author Elena Litani, IBM
39 */
40public class AugmentationsImpl implements Augmentations{
41
42    private AugmentationsItemsContainer fAugmentationsContainer =
43                                        new SmallContainer();
44
45    /**
46     * Add additional information identified by a key to the Augmentations structure.
47     *
48     * @param key    Identifier, can't be <code>null</code>
49     * @param item   Additional information
50     *
51     * @return the previous value of the specified key in the Augmentations strucutre,
52     *         or <code>null</code> if it did not have one.
53     */
54    public Object putItem (String key, Object item){
55        Object oldValue = fAugmentationsContainer.putItem(key, item);
56
57        if (oldValue == null && fAugmentationsContainer.isFull()) {
58            fAugmentationsContainer = fAugmentationsContainer.expand();
59        }
60
61        return oldValue;
62    }
63
64
65    /**
66     * Get information identified by a key from the Augmentations structure
67     *
68     * @param key    Identifier, can't be <code>null</code>
69     *
70     * @return the value to which the key is mapped in the Augmentations structure;
71     *         <code>null</code> if the key is not mapped to any value.
72     */
73    public Object getItem(String key){
74        return fAugmentationsContainer.getItem(key);
75    }
76
77
78    /**
79     * Remove additional info from the Augmentations structure
80     *
81     * @param key    Identifier, can't be <code>null</code>
82     */
83    public Object removeItem (String key){
84        return fAugmentationsContainer.removeItem(key);
85    }
86
87    /**
88     * Returns an enumeration of the keys in the Augmentations structure
89     *
90     */
91    public Enumeration keys (){
92        return fAugmentationsContainer.keys();
93    }
94
95    /**
96     * Remove all objects from the Augmentations structure.
97     */
98    public void removeAllItems() {
99        fAugmentationsContainer.clear();
100    }
101
102    public String toString() {
103        return fAugmentationsContainer.toString();
104    }
105
106    abstract class AugmentationsItemsContainer {
107        abstract public Object putItem(Object key, Object item);
108        abstract public Object getItem(Object key);
109        abstract public Object removeItem(Object key);
110        abstract public Enumeration keys();
111        abstract public void clear();
112        abstract public boolean isFull();
113        abstract public AugmentationsItemsContainer expand();
114    }
115
116    class SmallContainer extends AugmentationsItemsContainer {
117        final static int SIZE_LIMIT = 10;
118        final Object[] fAugmentations = new Object[SIZE_LIMIT*2];
119        int fNumEntries = 0;
120
121        public Enumeration keys() {
122            return new SmallContainerKeyEnumeration();
123        }
124
125        public Object getItem(Object key) {
126            for (int i = 0; i < fNumEntries*2; i = i + 2) {
127                if (fAugmentations[i].equals(key)) {
128                    return fAugmentations[i+1];
129                }
130            }
131
132            return null;
133        }
134
135        public Object putItem(Object key, Object item) {
136            for (int i = 0; i < fNumEntries*2; i = i + 2) {
137                if (fAugmentations[i].equals(key)) {
138                    Object oldValue = fAugmentations[i+1];
139                    fAugmentations[i+1] = item;
140
141                    return oldValue;
142                }
143            }
144
145            fAugmentations[fNumEntries*2] = key;
146            fAugmentations[fNumEntries*2+1] = item;
147            fNumEntries++;
148
149            return null;
150        }
151
152
153        public Object removeItem(Object key) {
154            for (int i = 0; i < fNumEntries*2; i = i + 2) {
155                if (fAugmentations[i].equals(key)) {
156                    Object oldValue = fAugmentations[i+1];
157
158                    for (int j = i; j < fNumEntries*2 - 2; j = j + 2) {
159                        fAugmentations[j] = fAugmentations[j+2];
160                        fAugmentations[j+1] = fAugmentations[j+3];
161                    }
162
163                    fAugmentations[fNumEntries*2-2] = null;
164                    fAugmentations[fNumEntries*2-1] = null;
165                    fNumEntries--;
166
167                    return oldValue;
168                }
169            }
170
171            return null;
172        }
173
174        public void clear() {
175            for (int i = 0; i < fNumEntries*2; i = i + 2) {
176                fAugmentations[i] = null;
177                fAugmentations[i+1] = null;
178            }
179
180            fNumEntries = 0;
181        }
182
183        public boolean isFull() {
184            return (fNumEntries == SIZE_LIMIT);
185        }
186
187        public AugmentationsItemsContainer expand() {
188            LargeContainer expandedContainer = new LargeContainer();
189
190            for (int i = 0; i < fNumEntries*2; i = i + 2) {
191                expandedContainer.putItem(fAugmentations[i],
192                                          fAugmentations[i+1]);
193            }
194
195            return expandedContainer;
196        }
197
198        public String toString() {
199            StringBuilder buff = new StringBuilder();
200            buff.append("SmallContainer - fNumEntries == ").append(fNumEntries);
201
202            for (int i = 0; i < SIZE_LIMIT*2; i=i+2) {
203                buff.append("\nfAugmentations[")
204                    .append(i)
205                    .append("] == ")
206                    .append(fAugmentations[i])
207                    .append("; fAugmentations[")
208                    .append(i+1)
209                    .append("] == ")
210                    .append(fAugmentations[i+1]);
211            }
212
213            return buff.toString();
214        }
215
216        class SmallContainerKeyEnumeration implements Enumeration {
217            Object [] enumArray = new Object[fNumEntries];
218            int next = 0;
219
220            SmallContainerKeyEnumeration() {
221                for (int i = 0; i < fNumEntries; i++) {
222                    enumArray[i] = fAugmentations[i*2];
223                }
224            }
225
226            public boolean hasMoreElements() {
227                return next < enumArray.length;
228            }
229
230            public Object nextElement() {
231                if (next >= enumArray.length) {
232                    throw new java.util.NoSuchElementException();
233                }
234
235                Object nextVal = enumArray[next];
236                enumArray[next] = null;
237                next++;
238
239                return nextVal;
240            }
241        }
242    }
243
244    class LargeContainer extends AugmentationsItemsContainer {
245        final Map<Object, Object> fAugmentations = new HashMap<>();
246
247        public Object getItem(Object key) {
248            return fAugmentations.get(key);
249        }
250
251        public Object putItem(Object key, Object item) {
252            return fAugmentations.put(key, item);
253        }
254
255        public Object removeItem(Object key) {
256            return fAugmentations.remove(key);
257        }
258
259        public Enumeration keys() {
260            return Collections.enumeration(fAugmentations.keySet());
261        }
262
263        public void clear() {
264            fAugmentations.clear();
265        }
266
267        public boolean isFull() {
268            return false;
269        }
270
271        public AugmentationsItemsContainer expand() {
272            return this;
273        }
274
275        public String toString() {
276            StringBuilder buff = new StringBuilder();
277            buff.append("LargeContainer");
278            for(Object key : fAugmentations.keySet()) {
279                buff.append("\nkey == ");
280                buff.append(key);
281                buff.append("; value == ");
282                buff.append(fAugmentations.get(key));
283            }
284            return buff.toString();
285        }
286    }
287}
288