001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
004: * All rights reserved.
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * $Id: SegmentedMap.java,v 1.3 2007/03/27 21:59:42 mlipp Exp $
021: *
022: * $Log: SegmentedMap.java,v $
023: * Revision 1.3 2007/03/27 21:59:42 mlipp
024: * Fixed lots of checkstyle warnings.
025: *
026: * Revision 1.2 2006/09/29 12:32:08 drmlipp
027: * Consistently using WfMOpen as projct name now.
028: *
029: * Revision 1.1.1.1 2003/06/30 20:05:12 drmlipp
030: * Initial import
031: *
032: * Revision 1.19 2003/06/27 08:51:47 lipp
033: * Fixed copyright/license information.
034: *
035: * Revision 1.18 2003/04/14 15:33:57 lipp
036: * Added query for real key.
037: *
038: * Revision 1.17 2003/04/13 19:02:20 lipp
039: * Fixed bug with not segmented key, cleaned code.
040: *
041: * Revision 1.16 2002/06/18 14:07:50 lipp
042: * Fixed bug in SegmentedMap that prevented the use of top level keys.
043: *
044: * Revision 1.15 2002/02/06 20:44:23 lipp
045: * Optimized by caching Set and fixed sync bug.
046: *
047: * Revision 1.14 2001/12/11 09:36:52 feldgen
048: * removed output-messages
049: *
050: * Revision 1.13 2001/12/04 16:43:24 feldgen
051: * cleaned import-statements
052: *
053: * Revision 1.12 2001/12/04 16:36:06 feldgen
054: * patched for use with ResourceBundleAsMap as HashMap
055: *
056: * Revision 1.11 2001/12/04 16:17:33 schlue
057: * Minor bug fix: No MissingResourceException is thrown any more
058: *
059: * Revision 1.10 2001/12/04 15:54:04 feldgen
060: * doccheck the second
061: *
062: * Revision 1.9 2001/12/04 15:36:47 lipp
063: * Removed unnecessary toString implementations.
064: *
065: * Revision 1.8 2001/12/04 11:52:48 schlue
066: * Minor bug fixes
067: *
068: * Revision 1.7 2001/12/04 10:12:02 feldgen
069: * doccheck the second
070: *
071: * Revision 1.6 2001/12/04 10:05:27 feldgen
072: * doccheck
073: *
074: * Revision 1.5 2001/12/04 08:49:50 feldgen
075: * doccheck
076: *
077: * Revision 1.4 2001/11/26 17:36:35 feldgen
078: * Code-fixing
079: *
080: * Revision 1.3 2001/11/26 08:34:35 feldgen
081: * Codestyle fixing
082: *
083: * Revision 1.2 2001/11/25 14:12:19 lipp
084: * Headers fixed.
085: *
086: */
087:
088: package de.danet.an.util;
089:
090: import java.util.Collection;
091: import java.util.HashMap;
092: import java.util.HashSet;
093: import java.util.Iterator;
094: import java.util.Map;
095: import java.util.Set;
096: import java.util.StringTokenizer;
097:
098: /**
099: * This class is used to filter paths from maps.
100: */
101:
102: public class SegmentedMap extends HashMap {
103: /**
104: * This class implements only the add-ons, needed for the segmentation
105: * feature.
106: */
107: private Map peer = null;
108:
109: /**
110: * This map caches the list of keys to a given path.
111: */
112: private Map path2keylist = null;
113:
114: /**
115: * This map caches the key to real key mapping.
116: */
117: private Map key2key = null;
118:
119: /**
120: * Default constructor.
121: * @param map map for handling requests
122: */
123: public SegmentedMap(Map map) {
124: peer = (HashMap) map;
125: path2keylist = new HashMap();
126: key2key = new HashMap();
127: }
128:
129: /**
130: * Delegating clear() to Map.
131: */
132: public void clear() {
133: key2key.clear();
134: path2keylist.clear();
135: peer.clear();
136: }
137:
138: /**
139: * Delegating containsKey(Object key) to Map.
140: * @param key The key that is looked for
141: * @return boolean if ResourceBundle contains this key
142: */
143: public boolean containsKey(Object key) {
144: return peer.containsKey(key);
145: }
146:
147: /**
148: * Delegating containsValue(Object value) to Map.
149: * @param value The value that is looked for
150: * @return boolean if ResourceBundle contains this value
151: */
152: public boolean containsValue(Object value) {
153: return peer.containsValue(value);
154: }
155:
156: /**
157: * Delegating entrySet() to Map.
158: * @return Set of Objects of this ResourceBundle
159: */
160: public Set entrySet() {
161: return peer.entrySet();
162: }
163:
164: /**
165: * Delegating equals(Object o) to Map.
166: * @param o The object to compare
167: * @return boolean if ResourceBundle equals Object
168: */
169: public boolean equals(Object o) {
170: return peer.equals(o);
171: }
172:
173: /**
174: * Returns the value of the "real" entry the given key resolves to.
175: * @param key the item to be looked up.
176: * @return the key of the item found.
177: */
178: public String resolvesTo(String key) {
179: String res = (String) key2key.get(key);
180: if (res != null) {
181: return res;
182: }
183: int attrStart = key.lastIndexOf('.') + 1;
184: String path = key.substring(0, attrStart);
185: String attribute = key.substring(attrStart);
186: while (true) {
187: String searchKey = path + attribute;
188: if (peer.containsKey(searchKey)) {
189: res = searchKey;
190: break;
191: }
192: if (path.length() == 0) {
193: break;
194: }
195: int endSegIdx = path.lastIndexOf('.', path.length() - 2);
196: if (endSegIdx < 0) {
197: path = "";
198: } else {
199: path = path.substring(0, endSegIdx + 1);
200: }
201: }
202: // Store in cache
203: // Make sure that in the meantime no concurrent thread has stored
204: // the same element
205: synchronized (key2key) {
206: key2key.put(key, res);
207: }
208: return res;
209: }
210:
211: /**
212: * Overwriting get(Object objectKey) of Map.
213: * @param objectKey the key which maps to the desired object
214: * @return Object matching key of this ResourceBundle
215: */
216: public Object get(Object objectKey) {
217: String real = resolvesTo((String) objectKey);
218: if (real == null) {
219: return null;
220: }
221: return peer.get(real);
222: }
223:
224: /**
225: * Delegating hashCode() to Map.
226: * @return int HashCode of the map
227: */
228: public int hashCode() {
229: return peer.hashCode();
230: }
231:
232: /**
233: * Delegating isEmpty() to Map.
234: * @return boolean if ResourceBundle is empty
235: */
236: public boolean isEmpty() {
237: return peer.isEmpty();
238: }
239:
240: /**
241: * Delegating keySet() to Map.
242: * @return Set of Objects of this ResourceBundle
243: */
244: public Set keySet() {
245: return peer.keySet();
246: }
247:
248: /**
249: * Delegating put(Object key, Object value) to Map.
250: * @param key the key mapping to the given object
251: * @param value the value mapped by the given key
252: * @return Object The Object previously mapped by this key
253: */
254: public Object put(Object key, Object value) {
255: if (!peer.containsKey(key)) {
256: key2key.clear();
257: path2keylist.clear();
258: }
259: return peer.put(key, value);
260: }
261:
262: /**
263: * Delegating putAll(Map t) to Map.
264: * @param t the map to put into the map
265: */
266: public void putAll(Map t) {
267: key2key.clear();
268: path2keylist.clear();
269: peer.putAll(t);
270: }
271:
272: /**
273: * Delegating remove(Object key) to Map.
274: * @param key the key which mapping is to be removed
275: * @return Object The Object that was mapped by the key
276: */
277: public Object remove(Object key) {
278: key2key.clear();
279: path2keylist.clear();
280: return peer.remove(key);
281: }
282:
283: /**
284: * Delegating size() to Map.
285: * @return int The number of key-mappings in this map
286: */
287: public int size() {
288: return peer.size();
289: }
290:
291: /**
292: * Delegating values() to Map.
293: * @return Collection Collection of the values
294: */
295: public Collection values() {
296: return peer.values();
297: }
298:
299: /**
300: * Overloading keySet() with keySet(String path).
301: * @param path Path to look for matching attributes
302: * @return Set Set of all matching keys
303: */
304: public Set keySet(String path) {
305: // If empty path is given, return all entries
306: if (path.length() == 0) {
307: return keySet();
308: }
309:
310: synchronized (path2keylist) {
311: // Check if keys to given path are already cached
312: if (path2keylist.containsKey(path)) {
313: return (Set) path2keylist.get(path);
314: }
315:
316: Set hs = new HashSet(peer.keySet());
317: Set pkeys = new HashSet();
318: final int pathlevel = new StringTokenizer(path, ".")
319: .countTokens();
320: for (Iterator i = hs.iterator(); i.hasNext();) {
321: final String key = (String) i.next();
322: final int keylevel = new StringTokenizer(key, ".")
323: .countTokens() - 1;
324:
325: // Build relevant path components
326: final int relevantPathlevel = Math.min(pathlevel,
327: keylevel);
328: final StringTokenizer ktok = new StringTokenizer(path,
329: ".");
330: StringBuffer relevantPath = new StringBuffer();
331: final StringTokenizer ptok = new StringTokenizer(key,
332: ".");
333: StringBuffer keyPath = new StringBuffer();
334: for (int level = relevantPathlevel; level > 0; level--) {
335: if (relevantPath.length() > 0) {
336: relevantPath.append(".");
337: }
338: relevantPath.append(ktok.nextToken());
339: if (keyPath.length() > 0) {
340: keyPath.append(".");
341: }
342: keyPath.append(ptok.nextToken());
343: }
344: // Check if path of key lies within given path
345: if (relevantPath.toString().equals(keyPath.toString())) {
346: // Find attribute component
347: final String attribute = key.substring(key
348: .lastIndexOf('.') + 1);
349: // Inheritance only needed if attribute comes from ancestor
350: final String inheritedKey = key.startsWith(path
351: + ".") ? key : path + "." + attribute;
352: // Add attribute if it isn't already defined
353: if (!pkeys.contains(inheritedKey)) {
354: pkeys.add(inheritedKey);
355: }
356: }
357: }
358: // Store list in cache
359: path2keylist.put(path, pkeys);
360: return pkeys;
361: }
362: }
363: }
|