Skip to contentMethod: toString()
1: /*
2: * JOPA
3: * Copyright (C) 2024 Czech Technical University in Prague
4: *
5: * This library is free software; you can redistribute it and/or
6: * modify it under the terms of the GNU Lesser General Public
7: * License as published by the Free Software Foundation; either
8: * version 3.0 of the License, or (at your option) any later version.
9: *
10: * This library is distributed in the hope that it will be useful,
11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13: * Lesser General Public License for more details.
14: *
15: * You should have received a copy of the GNU Lesser General Public
16: * License along with this library.
17: */
18: package cz.cvut.kbss.jopa.model;
19:
20: import java.io.Serializable;
21: import java.util.*;
22:
23: /**
24: * Represents a string with translations to (possibly) multiple languages.
25: * <p>
26: * This allows an application to naturally work in an internationalized environment leveraging the <a
27: * href="https://www.w3.org/TR/rdf11-concepts/#dfn-language-tagged-string">language-tagged strings</a> of the Semantic
28: * Web.
29: * <p>
30: * Instances of this class basically represent all available translations of the string, allowing to read (and write)
31: * particular versions by specifying the corresponding language tag. A special case supported by this class are <a
32: * href="https://www.w3.org/TR/rdf11-concepts/#section-Graph-Literal">simple literals</a> (language-less strings with
33: * type {@code xsd:string}), for which the language tag is {@code null}.
34: * <p>
35: * Note that this class is not thread-safe.
36: */
37: public class MultilingualString implements Serializable {
38:
39: private final Map<String, String> value;
40:
41: public MultilingualString() {
42: this.value = new HashMap<>(8);
43: }
44:
45: /**
46: * Creates a new {@code MultilingualString} instance based on the specified value.
47: * <p>
48: * Convenience constructor for simple initialization with existing data.
49: *
50: * @param value Map of language to translation values
51: */
52: public MultilingualString(Map<String, String> value) {
53: this.value = new HashMap<>(Objects.requireNonNull(value));
54: }
55:
56: /**
57: * Sets value in the specified language.
58: * <p>
59: * This overrides any previous value in the specified language, if it existed.
60: *
61: * @param language Language to use with the specified value. Passing {@code null} has the same effect as {@link
62: * #set(String)}
63: * @param value String value in the specified language
64: * @return This instance
65: * @see #set(String)
66: */
67: public MultilingualString set(String language, String value) {
68: Objects.requireNonNull(value);
69: this.value.put(language, value);
70: return this;
71: }
72:
73: /**
74: * Sets value without language.
75: * <p>
76: * That is, the specified value will be stored as a simple literal (type xsd:string).
77: *
78: * @param value Value to set
79: * @return This instance
80: * @see #set(String, String)
81: */
82: public MultilingualString set(String value) {
83: Objects.requireNonNull(value);
84: this.value.put(null, value);
85: return this;
86: }
87:
88: /**
89: * Gets value for the specified language.
90: * <p>
91: * If no language is specified, either the simple literal value is returned (if present), or any other existing
92: * value is returned. However, note that, in case of missing simple literal, repeated calls may return values in
93: * different languages. If there are no translations, {@code null} is returned.
94: *
95: * @param language Requested language (language tag). Can be {@code null}
96: * @return Value of this string for the specified language (or {@code null} if not available)
97: */
98: public String get(String language) {
99: return language != null ? value.get(language) : get();
100: }
101:
102: /**
103: * Gets the value of a simple literal represented by this instance.
104: * <p>
105: * If this instances does not represent a simple literal, any other existing value is returned. However, note that
106: * in that case repeated calls may return values in different languages.
107: * <p>
108: * If this object is empty (i.e., neither simple literal nor any translations are available), {@code null} is
109: * returned.
110: *
111: * @return Value of simple literal represented by this string
112: */
113: public String get() {
114: return value.getOrDefault(null, value.isEmpty() ? null : value.get(value.keySet().iterator().next()));
115: }
116:
117: /**
118: * Checks whether this string contains value in the specified language.
119: * <p>
120: * If no language is specified (argument is {@code null}), this method will return {@code true} if there is value
121: * without language tag (simple literal) or in any other language.
122: *
123: * @param language Requested language (language tag)
124: * @return {@code true} if this string exists in the specified language
125: */
126: public boolean contains(String language) {
127: return value.containsKey(language) || (language == null && !value.isEmpty());
128: }
129:
130: /**
131: * Checks whether this string contains a simple literal value.
132: * <p>
133: * That is, whether this instance contains a value without a language tag.
134: *
135: * @return {@code true} if and only if this string exists as a simple literal
136: */
137: public boolean containsSimple() {
138: return value.containsKey(null);
139: }
140:
141: /**
142: * Checks whether this instance does not contain any language representations (including a simple literal).
143: *
144: * @return {@code true} if no translations exist in this instance
145: */
146: public boolean isEmpty() {
147: return value.isEmpty();
148: }
149:
150: /**
151: * Removes translation in the specified language.
152: * <p>
153: * Passing in {@code null} will remove the simple literal contained in this instance.
154: *
155: * @param language Language for which translation should be removed
156: */
157: public void remove(String language) {
158: value.remove(language);
159: }
160:
161: /**
162: * Gets the set of languages for which translations exist in the instance.
163: * <p>
164: * Note that the result may contain {@code null}, indicating a simple literal.
165: *
166: * @return Set of languages available in this multilingual string
167: */
168: public Set<String> getLanguages() {
169: return Collections.unmodifiableSet(value.keySet());
170: }
171:
172: /**
173: * Gets the translations contained in this instance as a map of language-value pairs.
174: * <p>
175: * Convenience method for accessing all values at once. Note that this method returns the actual value used
176: * by this instance, so any changes to it will be reflected in the instance's behavior. It is not a general purpose
177: * method, rather a way to access and manipulate the underlying data structure more efficiently when necessary.
178: *
179: * @return Map of language to translation values
180: */
181: public Map<String, String> getValue() {
182: return value;
183: }
184:
185: @Override
186: public boolean equals(Object o) {
187: if (this == o) {
188: return true;
189: }
190: if (!(o instanceof MultilingualString)) {
191: return false;
192: }
193: MultilingualString that = (MultilingualString) o;
194: return value.equals(that.getValue());
195: }
196:
197: @Override
198: public int hashCode() {
199: return Objects.hash(value);
200: }
201:
202: @Override
203: public String toString() {
204: return value.toString();
205: }
206:
207: /**
208: * Creates a new instance of {@link MultilingualString} and sets the specified value in the specified language.
209: * <p>
210: * Convenience method for creating strings with one (initial) translation.
211: *
212: * @param value String value
213: * @param language Language of the value (language tag)
214: * @return New instance of {@code MultiLangString}
215: */
216: public static MultilingualString create(String value, String language) {
217: final MultilingualString instance = new MultilingualString();
218: instance.set(language, value);
219: return instance;
220: }
221: }