xref: /OpenGrok/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexAnalysisSettings3.java (revision e779faac6ac01c406f41e81a4354cd6ed590f968)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * See LICENSE.txt included in this distribution for the specific
9  * language governing permissions and limitations under the License.
10  *
11  * When distributing Covered Code, include this CDDL HEADER in each
12  * file and include the License file at LICENSE.txt.
13  * If applicable, add the following below this CDDL HEADER, with the
14  * fields enclosed by brackets "[]" replaced with your own identifying
15  * information: Portions Copyright [yyyy] [name of copyright owner]
16  *
17  * CDDL HEADER END
18  */
19 
20 /*
21  * Copyright (c) 2018, 2019, Chris Fraire <cfraire@me.com>.
22  */
23 package org.opengrok.indexer.index;
24 
25 import org.opengrok.indexer.util.WhitelistObjectInputFilter;
26 
27 import java.io.ByteArrayInputStream;
28 import java.io.ByteArrayOutputStream;
29 import java.io.IOException;
30 import java.io.ObjectInputFilter;
31 import java.io.ObjectInputStream;
32 import java.io.ObjectOutputStream;
33 import java.io.Serializable;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.Map;
37 
38 /**
39  * Represents a serializable gathering of some top-level metadata concerning the
40  * operation of {@link IndexDatabase} -- and persisted therein too -- which are
41  * re-compared upon each indexing run since changes to them might require
42  * re-indexing particular files or in certain cases all files.
43  */
44 public final class IndexAnalysisSettings3 implements Serializable {
45 
46     private static final long serialVersionUID = -4700122690707062276L;
47 
48     private static final ObjectInputFilter serialFilter = new WhitelistObjectInputFilter(IndexAnalysisSettings3.class);
49 
50     private String projectName;
51 
52     /**
53      * Nullable to allow easing this object into existing OpenGrok indexes
54      * without forcing a re-indexing.
55      * @serial
56      */
57     private Integer tabSize;
58 
59     /**
60      * Nullable to allow easing this object into existing OpenGrok indexes
61      * without forcing a re-indexing.
62      * @serial
63      */
64     private Long analyzerGuruVersion;
65 
66     /**
67      * Nullable because otherwise custom de-serialization does not work, as a
68      * {@code final} initialized value may not actually happen because Java
69      * de-serialization circumvents normal construction.
70      * @serial
71      */
72     private Map<String, Long> analyzersVersions = new HashMap<>();
73 
74     /**
75      * Nullable because otherwise custom de-serialization does not work, as a
76      * {@code final} initialized value may not actually happen because Java
77      * de-serialization circumvents normal construction. We don't bother with
78      * anything but a simple {@link HashMap} here.
79      * @serial
80      */
81     private Map<String, IndexedSymlink> indexedSymlinks = new HashMap<>();
82 
83     /**
84      * Gets the project name to be used to distinguish different instances of
85      * {@link IndexAnalysisSettings3} that might be returned by a Lucene
86      * {@code MultiReader} search across projects.
87      * @return projectName
88      */
getProjectName()89     public String getProjectName() {
90         return projectName;
91     }
92 
93     /**
94      * Sets the project name to be used to distinguish different instances of
95      * {@link IndexAnalysisSettings3} that might be returned by a Lucene
96      * {@code MultiReader} search across projects.
97      * @param value project name
98      */
setProjectName(String value)99     public void setProjectName(String value) {
100         this.projectName = value;
101     }
102 
getTabSize()103     public Integer getTabSize() {
104         return tabSize;
105     }
106 
setTabSize(Integer value)107     public void setTabSize(Integer value) {
108         this.tabSize = value;
109     }
110 
getAnalyzerGuruVersion()111     public Long getAnalyzerGuruVersion() {
112         return analyzerGuruVersion;
113     }
114 
setAnalyzerGuruVersion(Long value)115     public void setAnalyzerGuruVersion(Long value) {
116         this.analyzerGuruVersion = value;
117     }
118 
119     /**
120      * Gets the version number for the specified file type name if it exists.
121      * @param fileTypeName name of the file type
122      * @return a defined value or {@code null} if unknown
123      */
getAnalyzerVersion(String fileTypeName)124     public Long getAnalyzerVersion(String fileTypeName) {
125         return analyzersVersions.get(fileTypeName);
126     }
127 
128     /**
129      * Gets an unmodifiable view of the map of file type names to version
130      * numbers.
131      * @return a defined instance
132      */
getAnalyzersVersions()133     public Map<String, Long> getAnalyzersVersions() {
134         return Collections.unmodifiableMap(analyzersVersions);
135     }
136 
137     /**
138      * Replaces the contents of the instance's map with the {@code values}.
139      * @param values a defined instance
140      */
setAnalyzersVersions(Map<String, Long> values)141     public void setAnalyzersVersions(Map<String, Long> values) {
142         analyzersVersions.clear();
143         analyzersVersions.putAll(values);
144     }
145 
146     /**
147      * Gets an unmodifiable view of the map of canonical file names to symlinks.
148      * @return a defined instance
149      */
getIndexedSymlinks()150     public Map<String, IndexedSymlink> getIndexedSymlinks() {
151         return Collections.unmodifiableMap(indexedSymlinks);
152     }
153 
154     /**
155      * Replaces the contents of the instance's map with the {@code values}.
156      * @param values a defined instance
157      */
setIndexedSymlinks(Map<String, IndexedSymlink> values)158     public void setIndexedSymlinks(Map<String, IndexedSymlink> values) {
159         indexedSymlinks.clear();
160         indexedSymlinks.putAll(values);
161     }
162 
163     /**
164      * Creates a binary representation of this object.
165      * @return a byte array representing this object
166      * @throws  IOException Any exception thrown by the underlying
167      * OutputStream.
168      */
serialize()169     public byte[] serialize() throws IOException {
170         try (ByteArrayOutputStream bytes = new ByteArrayOutputStream(); var oos = new ObjectOutputStream(bytes)) {
171             oos.writeObject(this);
172             return bytes.toByteArray();
173         }
174     }
175 
176     /**
177      * De-serializes a binary representation of an {@link IndexAnalysisSettings3}
178      * object.
179      * @param bytes a byte array containing the serialization
180      * @return a defined instance
181      * @throws IOException Any of the usual Input/Output related exceptions.
182      * @throws ClassNotFoundException Class of a serialized object cannot be
183      * found.
184      * @throws ClassCastException if the array contains an object of another
185      * type than {@code IndexAnalysisSettings}
186      */
deserialize(byte[] bytes)187     public static IndexAnalysisSettings3 deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
188         try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
189             in.setObjectInputFilter(serialFilter);
190             return (IndexAnalysisSettings3) in.readObject();
191         }
192     }
193 
194     @SuppressWarnings("Duplicates")
readObject(ObjectInputStream in)195     private void readObject(ObjectInputStream in) throws ClassNotFoundException,
196             IOException {
197 
198         boolean hasValue = in.readBoolean();
199         String vString = in.readUTF();
200         projectName = hasValue ? vString : null;
201 
202         hasValue = in.readBoolean();
203         int vInteger = in.readInt();
204         tabSize = hasValue ? vInteger : null;
205 
206         hasValue = in.readBoolean();
207         long vLong = in.readLong();
208         analyzerGuruVersion = hasValue ? vLong : null;
209 
210         /*
211          * De-serialization circumvents normal construction, so the following
212          * field could be null.
213          */
214         if (analyzersVersions == null) {
215             analyzersVersions = new HashMap<>();
216         }
217         int analyzerCount = in.readInt();
218         for (int i = 0; i < analyzerCount; ++i) {
219             vString = in.readUTF();
220             vLong = in.readLong();
221             analyzersVersions.put(vString, vLong);
222         }
223 
224         /*
225          * De-serialization circumvents normal construction, so the following
226          * field could be null.
227          */
228         if (indexedSymlinks == null) {
229             indexedSymlinks = new HashMap<>();
230         }
231         int symlinkCount = in.readInt();
232         for (int i = 0; i < symlinkCount; ++i) {
233             String absolute = in.readUTF();
234             String canonical = in.readUTF();
235             boolean isLocal = in.readBoolean();
236             IndexedSymlink indexed = new IndexedSymlink(absolute, canonical, isLocal);
237             indexedSymlinks.put(canonical, indexed);
238         }
239     }
240 
241     @SuppressWarnings("Duplicates")
writeObject(ObjectOutputStream out)242     private void writeObject(ObjectOutputStream out) throws IOException {
243         out.writeBoolean(projectName != null); // hasValue
244         out.writeUTF(projectName == null ? "" : projectName);
245 
246         out.writeBoolean(tabSize != null); // hasValue
247         out.writeInt(tabSize == null ? 0 : tabSize);
248 
249         out.writeBoolean(analyzerGuruVersion != null); // hasValue
250         out.writeLong(analyzerGuruVersion == null ? 0 : analyzerGuruVersion);
251 
252         int collectionCount = analyzersVersions.size();
253         out.writeInt(collectionCount);
254         for (Map.Entry<String, Long> entry : analyzersVersions.entrySet()) {
255             out.writeUTF(entry.getKey());
256             out.writeLong(entry.getValue());
257             --collectionCount;
258         }
259         if (collectionCount != 0) {
260             throw new IllegalStateException("analyzersVersions were modified");
261         }
262 
263         collectionCount = indexedSymlinks.size();
264         out.writeInt(collectionCount);
265         for (IndexedSymlink entry : indexedSymlinks.values()) {
266             out.writeUTF(entry.getAbsolute());
267             out.writeUTF(entry.getCanonical());
268             out.writeBoolean(entry.isLocal());
269             --collectionCount;
270         }
271         if (collectionCount != 0) {
272             throw new IllegalStateException("indexedSymlinks were modified");
273         }
274     }
275 }
276