View Javadoc
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 }