1 /*
2 * Copyright 2012-2025 CodeLibs Project and the Others.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13 * either express or implied. See the License for the specific language
14 * governing permissions and limitations under the License.
15 */
16 package org.codelibs.fess.util;
17
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.LinkedHashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
26 /**
27 * A wrapper implementation of Map<String, Object> that provides special handling
28 * for document data. This class wraps an existing map and provides custom behavior
29 * for the entrySet method to ensure language fields appear first in iteration order.
30 * Used throughout the Fess search system for document data manipulation.
31 *
32 */
33 public class DocMap implements Map<String, Object> {
34
35 /** The key used for language field identification */
36 private static final String LANG_KEY = "lang";
37
38 /** The underlying map that this DocMap wraps */
39 private final Map<String, Object> parent;
40
41 /**
42 * Constructor that creates a DocMap wrapping the given parent map.
43 *
44 * @param parent the map to wrap and delegate operations to
45 */
46 public DocMap(final Map<String, Object> parent) {
47 this.parent = parent;
48 }
49
50 /**
51 * Returns the number of key-value mappings in this map.
52 *
53 * @return the number of key-value mappings in this map
54 */
55 @Override
56 public int size() {
57 return parent.size();
58 }
59
60 /**
61 * Returns true if this map contains no key-value mappings.
62 *
63 * @return true if this map contains no key-value mappings
64 */
65 @Override
66 public boolean isEmpty() {
67 return parent.isEmpty();
68 }
69
70 /**
71 * Returns true if this map contains a mapping for the specified key.
72 *
73 * @param key the key whose presence in this map is to be tested
74 * @return true if this map contains a mapping for the specified key
75 */
76 @Override
77 public boolean containsKey(final Object key) {
78 return parent.containsKey(key);
79 }
80
81 /**
82 * Returns true if this map maps one or more keys to the specified value.
83 *
84 * @param value the value whose presence in this map is to be tested
85 * @return true if this map maps one or more keys to the specified value
86 */
87 @Override
88 public boolean containsValue(final Object value) {
89 return parent.containsValue(value);
90 }
91
92 /**
93 * Returns the value to which the specified key is mapped.
94 *
95 * @param key the key whose associated value is to be returned
96 * @return the value to which the specified key is mapped, or null if no mapping exists
97 */
98 @Override
99 public Object get(final Object key) {
100 return parent.get(key);
101 }
102
103 /**
104 * Associates the specified value with the specified key in this map.
105 *
106 * @param key the key with which the specified value is to be associated
107 * @param value the value to be associated with the specified key
108 * @return the previous value associated with key, or null if no mapping existed
109 */
110 @Override
111 public Object put(final String key, final Object value) {
112 return parent.put(key, value);
113 }
114
115 /**
116 * Removes the mapping for a key from this map if it is present.
117 *
118 * @param key the key whose mapping is to be removed from the map
119 * @return the previous value associated with key, or null if no mapping existed
120 */
121 @Override
122 public Object remove(final Object key) {
123 return parent.remove(key);
124 }
125
126 /**
127 * Copies all of the mappings from the specified map to this map.
128 *
129 * @param m the mappings to be stored in this map
130 */
131 @Override
132 public void putAll(final Map<? extends String, ? extends Object> m) {
133 parent.putAll(m);
134 }
135
136 /**
137 * Removes all of the mappings from this map.
138 */
139 @Override
140 public void clear() {
141 parent.clear();
142 }
143
144 /**
145 * Returns a Set view of the keys contained in this map.
146 *
147 * @return a set view of the keys contained in this map
148 */
149 @Override
150 public Set<String> keySet() {
151 return parent.keySet();
152 }
153
154 /**
155 * Returns a Collection view of the values contained in this map.
156 *
157 * @return a collection view of the values contained in this map
158 */
159 @Override
160 public Collection<Object> values() {
161 return parent.values();
162 }
163
164 /**
165 * Returns a Set view of the mappings contained in this map.
166 * If the map contains a language key, it will be sorted to appear first.
167 *
168 * @return a set view of the mappings contained in this map with language key prioritized
169 */
170 @Override
171 public Set<java.util.Map.Entry<String, Object>> entrySet() {
172 if (parent.containsKey(LANG_KEY)) {
173 final List<java.util.Map.Entry<String, Object>> list = new ArrayList<>(parent.entrySet());
174 Collections.sort(list, (o1, o2) -> {
175 final String k1 = o1.getKey();
176 if (LANG_KEY.equals(k1)) {
177 return -1;
178 }
179 final String k2 = o2.getKey();
180 if (LANG_KEY.equals(k2)) {
181 return -1;
182 }
183 return k1.compareTo(k2);
184 });
185 return new LinkedHashSet<>(list);
186 }
187 return parent.entrySet();
188 }
189
190 }