View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.index.reader;
20  
21  import java.util.HashMap;
22  import java.util.Map;
23  import java.util.function.Function;
24  
25  import org.apache.maven.index.reader.Record.Type;
26  
27  import static org.apache.maven.index.reader.Utils.FIELD_SEPARATOR;
28  import static org.apache.maven.index.reader.Utils.INFO;
29  import static org.apache.maven.index.reader.Utils.UINFO;
30  import static org.apache.maven.index.reader.Utils.nvl;
31  
32  /**
33   * Maven Index record transformer, that transforms {@link Record}s into "native" Maven Indexer records.
34   *
35   * @since 5.1.2
36   */
37  public class RecordCompactor implements Function<Record, Map<String, String>> {
38      /**
39       * Compacts {@link Record} into low level MI record with all the encoded fields as physically present in MI binary
40       * chunk.
41       */
42      @Override
43      public Map<String, String> apply(final Record record) {
44          if (Type.DESCRIPTOR == record.getType()) {
45              return compactDescriptor(record);
46          } else if (Type.ALL_GROUPS == record.getType()) {
47              return compactAllGroups(record);
48          } else if (Type.ROOT_GROUPS == record.getType()) {
49              return compactRootGroups(record);
50          } else if (Type.ARTIFACT_REMOVE == record.getType()) {
51              return compactDeletedArtifact(record);
52          } else if (Type.ARTIFACT_ADD == record.getType()) {
53              return compactAddedArtifact(record);
54          } else {
55              throw new IllegalArgumentException("Unknown record: " + record);
56          }
57      }
58  
59      private static Map<String, String> compactDescriptor(final Record record) {
60          final Map<String, String> result = new HashMap<>();
61          result.put("DESCRIPTOR", "NexusIndex");
62          result.put("IDXINFO", "1.0|" + record.getString(Record.REPOSITORY_ID));
63          return result;
64      }
65  
66      private static Map<String, String> compactAllGroups(final Record record) {
67          final Map<String, String> result = new HashMap<>();
68          result.put("allGroups", "allGroups");
69          putIfNotNullAsStringArray(record.getStringArray(Record.ALL_GROUPS), result, "allGroupsList");
70          return result;
71      }
72  
73      private static Map<String, String> compactRootGroups(final Record record) {
74          final Map<String, String> result = new HashMap<>();
75          result.put("rootGroups", "allGroups");
76          putIfNotNullAsStringArray(record.getStringArray(Record.ROOT_GROUPS), result, "rootGroupsList");
77          return result;
78      }
79  
80      private static Map<String, String> compactDeletedArtifact(final Record record) {
81          final Map<String, String> result = new HashMap<>();
82          putIfNotNullTS(record.getLong(Record.REC_MODIFIED), result, "m");
83          result.put("del", compactUinfo(record));
84          return result;
85      }
86  
87      /**
88       * Expands the "encoded" Maven Indexer record by splitting the synthetic fields and applying expanded field naming.
89       */
90      private static Map<String, String> compactAddedArtifact(final Record record) {
91          final Map<String, String> result = new HashMap<>();
92  
93          // Minimal
94          result.put(UINFO, compactUinfo(record));
95  
96          String info = nvl(record.getString(Record.PACKAGING))
97                  + FIELD_SEPARATOR
98                  + record.getLong(Record.FILE_MODIFIED)
99                  + FIELD_SEPARATOR
100                 + record.getLong(Record.FILE_SIZE)
101                 + FIELD_SEPARATOR
102                 + (record.getBoolean(Record.HAS_SOURCES) ? "1" : "0")
103                 + FIELD_SEPARATOR
104                 + (record.getBoolean(Record.HAS_JAVADOC) ? "1" : "0")
105                 + FIELD_SEPARATOR
106                 + (record.getBoolean(Record.HAS_SIGNATURE) ? "1" : "0")
107                 + FIELD_SEPARATOR
108                 + nvl(record.getString(Record.FILE_EXTENSION));
109         result.put(INFO, info);
110 
111         putIfNotNullTS(record.getLong(Record.REC_MODIFIED), result, "m");
112         putIfNotNull(record.getString(Record.NAME), result, "n");
113         putIfNotNull(record.getString(Record.DESCRIPTION), result, "d");
114         putIfNotNull(record.getString(Record.SHA1), result, "1");
115 
116         // Jar file contents (optional)
117         putIfNotNullAsStringArray(record.getStringArray(Record.CLASSNAMES), result, "classnames");
118 
119         // Maven Plugin (optional)
120         putIfNotNull(record.getString(Record.PLUGIN_PREFIX), result, "px");
121         putIfNotNullAsStringArray(record.getStringArray(Record.PLUGIN_GOALS), result, "gx");
122 
123         // OSGi (optional)
124         putIfNotNull(record.getString(Record.OSGI_BUNDLE_SYMBOLIC_NAME), result, "Bundle-SymbolicName");
125         putIfNotNull(record.getString(Record.OSGI_BUNDLE_VERSION), result, "Bundle-Version");
126         putIfNotNull(record.getString(Record.OSGI_EXPORT_PACKAGE), result, "Export-Package");
127         putIfNotNull(record.getString(Record.OSGI_EXPORT_SERVICE), result, "Export-Service");
128         putIfNotNull(record.getString(Record.OSGI_BUNDLE_DESCRIPTION), result, "Bundle-Description");
129         putIfNotNull(record.getString(Record.OSGI_BUNDLE_NAME), result, "Bundle-Name");
130         putIfNotNull(record.getString(Record.OSGI_BUNDLE_LICENSE), result, "Bundle-License");
131         putIfNotNull(record.getString(Record.OSGI_EXPORT_DOCURL), result, "Bundle-DocURL");
132         putIfNotNull(record.getString(Record.OSGI_IMPORT_PACKAGE), result, "Import-Package");
133         putIfNotNull(record.getString(Record.OSGI_REQUIRE_BUNDLE), result, "Require-Bundle");
134         putIfNotNull(record.getString(Record.OSGI_PROVIDE_CAPABILITY), result, "Provide-Capability");
135         putIfNotNull(record.getString(Record.OSGI_REQUIRE_CAPABILITY), result, "Require-Capability");
136         putIfNotNull(record.getString(Record.OSGI_FRAGMENT_HOST), result, "Fragment-Host");
137         putIfNotNull(record.getString(Record.OSGI_BREE), result, "Bundle-RequiredExecutionEnvironment");
138         putIfNotNull(record.getString(Record.SHA_256), result, "sha256");
139 
140         return result;
141     }
142 
143     /**
144      * Creates UINFO synthetic field.
145      */
146     private static String compactUinfo(final Record record) {
147         final String classifier = record.getString(Record.CLASSIFIER);
148         StringBuilder sb = new StringBuilder()
149                 .append(record.getString(Record.GROUP_ID))
150                 .append(FIELD_SEPARATOR)
151                 .append(record.getString(Record.ARTIFACT_ID))
152                 .append(FIELD_SEPARATOR)
153                 .append(record.getString(Record.VERSION))
154                 .append(FIELD_SEPARATOR)
155                 .append(nvl(classifier));
156         if (classifier != null) {
157             sb.append(FIELD_SEPARATOR).append(record.get(Record.FILE_EXTENSION));
158         }
159         return sb.toString();
160     }
161 
162     /**
163      * Helper to put a value from source map into target map, if not null.
164      */
165     private static void putIfNotNull(final String source, final Map<String, String> target, final String targetName) {
166         if (source != null) {
167             target.put(targetName, source);
168         }
169     }
170 
171     /**
172      * Helper to put a {@link Long} value from source map into target map, if not null.
173      */
174     private static void putIfNotNullTS(final Long source, final Map<String, String> target, final String targetName) {
175         if (source != null) {
176             target.put(targetName, String.valueOf(source));
177         }
178     }
179 
180     /**
181      * Helper to put a array value from source map into target map joined with {@link Utils#FIELD_SEPARATOR}, if not
182      * null.
183      */
184     private static void putIfNotNullAsStringArray(
185             final String[] source, final Map<String, String> target, final String targetName) {
186         if (source != null && source.length > 0) {
187             StringBuilder sb = new StringBuilder();
188             sb.append(source[0]);
189             for (int i = 1; i < source.length; i++) {
190                 sb.append(FIELD_SEPARATOR).append(source[i]);
191             }
192             target.put(targetName, sb.toString());
193         }
194     }
195 }