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.synonym;
17
18 import java.util.Arrays;
19 import java.util.Objects;
20
21 import org.apache.commons.lang3.StringUtils;
22 import org.codelibs.core.collection.ArrayUtil;
23 import org.codelibs.core.lang.StringUtil;
24 import org.codelibs.fess.dict.DictionaryItem;
25
26 /**
27 * Represents an item in a synonym dictionary.
28 * This class stores a set of input words and their corresponding output synonyms.
29 * It also tracks updated values for the item.
30 */
31 public class SynonymItem extends DictionaryItem {
32 /** The original input words. */
33 private final String[] inputs;
34
35 /** The original output synonyms. */
36 private final String[] outputs;
37
38 /** The new input words, if updated. */
39 private String[] newInputs;
40
41 /** The new output synonyms, if updated. */
42 private String[] newOutputs;
43
44 /**
45 * Constructs a new synonym item.
46 *
47 * @param id The unique identifier of the item.
48 * @param inputs The input words.
49 * @param outputs The output synonyms.
50 */
51 public SynonymItem(final long id, final String[] inputs, final String[] outputs) {
52 this.id = id;
53 this.inputs = inputs;
54 this.outputs = outputs;
55
56 if (id == 0) {
57 // create
58 newInputs = inputs;
59 newOutputs = outputs;
60 }
61 }
62
63 /**
64 * Gets the new input words.
65 * Returns a defensive copy to prevent external modification.
66 *
67 * @return The new input words (defensive copy).
68 */
69 public String[] getNewInputs() {
70 return newInputs == null ? null : newInputs.clone();
71 }
72
73 /**
74 * Sets the new input words.
75 *
76 * @param newInputs The new input words.
77 */
78 public void setNewInputs(final String[] newInputs) {
79 this.newInputs = newInputs;
80 }
81
82 /**
83 * Gets the new output synonyms.
84 * Returns a defensive copy to prevent external modification.
85 *
86 * @return The new output synonyms (defensive copy).
87 */
88 public String[] getNewOutputs() {
89 return newOutputs == null ? null : newOutputs.clone();
90 }
91
92 /**
93 * Sets the new output synonyms.
94 *
95 * @param newOutputs The new output synonyms.
96 */
97 public void setNewOutputs(final String[] newOutputs) {
98 this.newOutputs = newOutputs;
99 }
100
101 /**
102 * Gets the original input words.
103 * Returns a defensive copy to prevent external modification.
104 *
105 * @return The original input words (defensive copy).
106 */
107 public String[] getInputs() {
108 return inputs == null ? null : inputs.clone();
109 }
110
111 /**
112 * Gets the input words as a newline-separated string.
113 *
114 * @return The input words as a string.
115 */
116 public String getInputsValue() {
117 if (inputs == null) {
118 return StringUtil.EMPTY;
119 }
120 return String.join("\n", inputs);
121 }
122
123 /**
124 * Gets the original output synonyms.
125 * Returns a defensive copy to prevent external modification.
126 *
127 * @return The original output synonyms (defensive copy).
128 */
129 public String[] getOutputs() {
130 return outputs == null ? null : outputs.clone();
131 }
132
133 /**
134 * Gets the output synonyms as a newline-separated string.
135 *
136 * @return The output synonyms as a string.
137 */
138 public String getOutputsValue() {
139 if (outputs == null) {
140 return StringUtil.EMPTY;
141 }
142 return String.join("\n", outputs);
143 }
144
145 /**
146 * Checks if the item has been updated.
147 *
148 * @return true if the item has been updated, false otherwise.
149 */
150 public boolean isUpdated() {
151 return newInputs != null && newOutputs != null;
152 }
153
154 /**
155 * Checks if the item has been marked for deletion.
156 *
157 * @return true if the item is marked for deletion, false otherwise.
158 */
159 public boolean isDeleted() {
160 return isUpdated() && newInputs.length == 0;
161 }
162
163 @Override
164 public int hashCode() {
165 return Objects.hash(Arrays.hashCode(inputs), Arrays.hashCode(outputs));
166 }
167
168 @Override
169 public boolean equals(final Object obj) {
170 if (this == obj) {
171 return true;
172 }
173 if (obj == null || getClass() != obj.getClass()) {
174 return false;
175 }
176 final SynonymItem other = (SynonymItem) obj;
177 if (!ArrayUtil.equalsIgnoreSequence(inputs, other.inputs) || !ArrayUtil.equalsIgnoreSequence(outputs, other.outputs)) {
178 return false;
179 }
180 return true;
181 }
182
183 @Override
184 public String toString() {
185 return "SynonymItem [id=" + id + ", inputs=" + Arrays.toString(inputs) + ", outputs=" + Arrays.toString(outputs) + ", newInputs="
186 + Arrays.toString(newInputs) + ", newOutputs=" + Arrays.toString(newOutputs) + "]";
187 }
188
189 /**
190 * Converts the item to a string representation for writing to a file.
191 *
192 * @return A string in the format "input1,input2=>output1,output2".
193 */
194 public String toLineString() {
195 if (isUpdated()) {
196 if (Arrays.equals(newInputs, newOutputs)) {
197 return StringUtils.join(newInputs, ",");
198 }
199 return StringUtils.join(newInputs, ",") + "=>" + StringUtils.join(newOutputs, ",");
200 }
201 if (Arrays.equals(inputs, outputs)) {
202 return StringUtils.join(inputs, ",");
203 }
204 return StringUtils.join(inputs, ",") + "=>" + StringUtils.join(outputs, ",");
205 }
206
207 }