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.dict.mapping;
17  
18  import java.util.Arrays;
19  import java.util.Objects;
20  
21  import org.apache.commons.lang3.StringUtils;
22  import org.codelibs.core.lang.StringUtil;
23  import org.codelibs.fess.dict.DictionaryItem;
24  
25  /**
26   * Represents a single character mapping rule that defines how input characters are mapped to output characters
27   * for text analysis and search processing. This class is used in character mapping dictionaries to transform
28   * text during indexing and search operations.
29   *
30   * <p>Each mapping item consists of one or more input character sequences that are mapped to a single output
31   * character sequence. The mapping supports both original values and new values for update operations.</p>
32   */
33  public class CharMappingItem extends DictionaryItem {
34      /**
35       * Array of input character sequences that will be mapped to the output sequence.
36       * These represent the original/current input values for this mapping rule.
37       */
38      private final String[] inputs;
39  
40      /**
41       * The output character sequence that input characters will be mapped to.
42       * This represents the original/current output value for this mapping rule.
43       */
44      private final String output;
45  
46      /**
47       * Array of new input character sequences for update operations.
48       * When not null, indicates this item has pending updates.
49       */
50      private String[] newInputs;
51  
52      /**
53       * The new output character sequence for update operations.
54       * When not null, indicates this item has pending updates.
55       */
56      private String newOutput;
57  
58      /**
59       * Constructs a new CharMappingItem with the specified ID, input sequences, and output sequence.
60       *
61       * @param id the unique identifier for this mapping item
62       * @param inputs array of input character sequences that will be mapped to the output
63       * @param output the output character sequence that inputs will be mapped to
64       */
65      public CharMappingItem(final long id, final String[] inputs, final String output) {
66          this.id = id;
67          this.inputs = inputs;
68          this.output = output == null ? null : output.replace("\n", " ");
69          Arrays.sort(inputs);
70  
71          if (id == 0) {
72              // create
73              newInputs = inputs;
74              newOutput = output;
75          }
76      }
77  
78      /**
79       * Returns the array of new input character sequences for update operations.
80       * Returns a defensive copy to prevent external modification.
81       *
82       * @return array of new input sequences (defensive copy), or null if no updates are pending
83       */
84      public String[] getNewInputs() {
85          return newInputs == null ? null : newInputs.clone();
86      }
87  
88      /**
89       * Sets the array of new input character sequences for update operations.
90       *
91       * @param newInputs array of new input sequences to set
92       */
93      public void setNewInputs(final String[] newInputs) {
94          this.newInputs = newInputs;
95      }
96  
97      /**
98       * Returns the new output character sequence for update operations.
99       *
100      * @return the new output sequence, or null if no updates are pending
101      */
102     public String getNewOutput() {
103         return newOutput;
104     }
105 
106     /**
107      * Sets the new output character sequence for update operations.
108      * Newline characters in the output are automatically replaced with spaces.
109      *
110      * @param newOutput the new output sequence to set
111      */
112     public void setNewOutput(final String newOutput) {
113         this.newOutput = newOutput == null ? null : newOutput.replace("\n", " ");
114     }
115 
116     /**
117      * Returns the array of input character sequences that are mapped to the output.
118      * Returns a defensive copy to prevent external modification.
119      *
120      * @return array of input sequences (defensive copy)
121      */
122     public String[] getInputs() {
123         return inputs == null ? null : inputs.clone();
124     }
125 
126     /**
127      * Returns all input sequences joined with newline characters as a single string.
128      * This is useful for display purposes in forms and user interfaces.
129      *
130      * @return string representation of all inputs separated by newlines, or empty string if inputs is null
131      */
132     public String getInputsValue() {
133         if (inputs == null) {
134             return StringUtil.EMPTY;
135         }
136         return String.join("\n", inputs);
137     }
138 
139     /**
140      * Returns the output character sequence that inputs are mapped to.
141      *
142      * @return the output sequence
143      */
144     public String getOutput() {
145         return output;
146     }
147 
148     /**
149      * Checks whether this mapping item has pending updates.
150      *
151      * @return true if both newInputs and newOutput are not null, indicating pending updates
152      */
153     public boolean isUpdated() {
154         return newInputs != null && newOutput != null;
155     }
156 
157     /**
158      * Checks whether this mapping item is marked for deletion.
159      * An item is considered deleted if it has updates pending and the new inputs array is empty.
160      *
161      * @return true if the item is marked for deletion
162      */
163     public boolean isDeleted() {
164         return isUpdated() && newInputs.length == 0;
165     }
166 
167     /**
168      * Sorts both the current inputs and new inputs arrays in place.
169      * This ensures consistent ordering for comparison and equality operations.
170      */
171     public void sort() {
172         if (inputs != null) {
173             Arrays.sort(inputs);
174         }
175         if (newInputs != null) {
176             Arrays.sort(newInputs);
177         }
178     }
179 
180     /**
181      * Calculates the hash code for this CharMappingItem based on inputs and output.
182      *
183      * @return the hash code value for this object
184      */
185     @Override
186     public int hashCode() {
187         return Objects.hash(Arrays.hashCode(inputs), output);
188     }
189 
190     /**
191      * Compares this CharMappingItem with another object for equality.
192      * Two CharMappingItem objects are equal if they have the same inputs and output.
193      * Note: inputs arrays are sorted in the constructor, so no sorting is needed here.
194      *
195      * @param obj the object to compare with
196      * @return true if the objects are equal, false otherwise
197      */
198     @Override
199     public boolean equals(final Object obj) {
200         if (this == obj) {
201             return true;
202         }
203         if (obj == null || getClass() != obj.getClass()) {
204             return false;
205         }
206         final CharMappingItem other = (CharMappingItem) obj;
207         if (!Arrays.equals(inputs, other.inputs)) {
208             return false;
209         }
210         return Objects.equals(output, other.output);
211     }
212 
213     /**
214      * Returns a string representation of this CharMappingItem including all fields.
215      *
216      * @return string representation of this object
217      */
218     @Override
219     public String toString() {
220         return "MappingItem [id=" + id + ", inputs=" + Arrays.toString(inputs) + ", output=" + output + ", newInputs="
221                 + Arrays.toString(newInputs) + ", newOutput=" + newOutput + "]";
222     }
223 
224     /**
225      * Returns a compact string representation of this mapping item in the format "input1,input2=>output".
226      * If the item has pending updates, the new values are used; otherwise, the current values are used.
227      *
228      * @return compact string representation of the mapping rule
229      */
230     public String toLineString() {
231         if (isUpdated()) {
232             return StringUtils.join(newInputs, ",") + "=>" + newOutput;
233         }
234         return StringUtils.join(inputs, ",") + "=>" + output;
235     }
236 
237 }