efa7e0aa1acc39f938bd8e8a4c3fe9b869a5f265
[sdc.git] /
1 /*
2  * Copyright © 2016-2017 European Support Limited
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, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.openecomp.sdc.tosca.csar;
18
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.ImmutableMap;
21 import org.openecomp.sdc.common.errors.Messages;
22 import org.openecomp.sdc.logging.api.Logger;
23 import org.openecomp.sdc.logging.api.LoggerFactory;
24
25 import java.io.BufferedReader;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.nio.charset.StandardCharsets;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36
37 import static org.openecomp.core.validation.errors.ErrorMessagesFormatBuilder.getErrorWithParameters;
38 import static org.openecomp.sdc.tosca.csar.CSARConstants.METADATA_MF_ATTRIBUTE;
39 import static org.openecomp.sdc.tosca.csar.CSARConstants.NON_MANO_MF_ATTRIBUTE;
40 import static org.openecomp.sdc.tosca.csar.CSARConstants.SEPERATOR_MF_ATTRIBUTE;
41 import static org.openecomp.sdc.tosca.csar.CSARConstants.SOURCE_MF_ATTRIBUTE;
42
43 public class OnboardingManifest implements Manifest{
44     private static final Logger LOGGER = LoggerFactory.getLogger(OnboardingManifest.class);
45     private Map<String, String> metadata;
46     private List<String> sources;
47     private List<String> errors;
48     private Map<String, List<String>> nonManoSources;
49
50     private OnboardingManifest() {
51         errors = new ArrayList<>();
52         sources = new ArrayList<>();
53         metadata = new HashMap<>();
54         nonManoSources = new HashMap<>();
55     }
56
57     /**
58      * This Method will parse manifest, extracting fields mandatory/non-mandatory,
59      * if error occurred it's recorded and will be used for deciding if manifest is valid
60      * @param is manifest file input stream
61      * @return Manifest object
62      */
63     public static Manifest parse(InputStream is) {
64         OnboardingManifest manifest = new OnboardingManifest();
65         try {
66             ImmutableList<String> lines = manifest.readAllLines(is);
67             manifest.processManifest(lines);
68         } catch (IOException e){
69             LOGGER.error(e.getMessage(),e);
70             manifest.errors.add(Messages.MANIFEST_PARSER_INTERNAL.getErrorMessage());
71         }
72         return manifest;
73     }
74
75     private void processManifest(ImmutableList<String> lines) {
76         if(lines == null || lines.isEmpty()){
77             errors.add(Messages.MANIFEST_EMPTY.getErrorMessage());
78             return;
79         }
80         Iterator<String> iterator = lines.iterator();
81         //SOL004 #4.3.2: The manifest file shall start with the package metadata
82         String line = iterator.next();
83         if(!line.trim().equals(METADATA_MF_ATTRIBUTE + SEPERATOR_MF_ATTRIBUTE)){
84             reportError(line);
85             return;
86         }
87         //handle metadata
88         processMetadata(iterator);
89
90         if (errors.isEmpty()) {
91             if (metadata.isEmpty()) {
92                 errors.add(Messages.MANIFEST_NO_METADATA.getErrorMessage());
93             }
94             if (sources.isEmpty()) {
95                 errors.add(Messages.MANIFEST_NO_SOURCES.getErrorMessage());
96             }
97         }
98     }
99
100     private void processSourcesAndNonManoSources(Iterator<String> iterator, String prevLine) {
101         if(prevLine.isEmpty()){
102             if(iterator.hasNext()){
103                 processSourcesAndNonManoSources(iterator, iterator.next());
104             }
105         }else if(prevLine.startsWith(SOURCE_MF_ATTRIBUTE+SEPERATOR_MF_ATTRIBUTE)){
106             processSource(iterator, prevLine);
107         } else if(prevLine.startsWith(NON_MANO_MF_ATTRIBUTE+SEPERATOR_MF_ATTRIBUTE)){
108             //non mano should be the last bit in manifest file,
109             // all sources after non mano will be placed to the last non mano
110             // key, if any other structure met error reported
111             processNonManoInputs(iterator, iterator.next());
112         }else{
113             reportError(prevLine);
114         }
115     }
116
117     private void processSource(Iterator<String> iterator, String prevLine) {
118         String value = prevLine.substring((SOURCE_MF_ATTRIBUTE + SEPERATOR_MF_ATTRIBUTE).length()).trim();
119         sources.add(value);
120         if(iterator.hasNext()) {
121             processSourcesAndNonManoSources(iterator, iterator.next());
122         }
123     }
124
125     private void processMetadata(Iterator<String> iterator) {
126         if(!iterator.hasNext()){
127             return;
128         }
129        String line = iterator.next();
130        if(line.isEmpty()){
131            processMetadata(iterator);
132            return;
133        }
134        String[] metaSplit = line.split(SEPERATOR_MF_ATTRIBUTE);
135         if (metaSplit.length < 2){
136             reportError(line);
137             return;
138         }
139         if (!metaSplit[0].equals(SOURCE_MF_ATTRIBUTE) && !metaSplit[0].equals(NON_MANO_MF_ATTRIBUTE)){
140             String value = line.substring((metaSplit[0] + SEPERATOR_MF_ATTRIBUTE).length()).trim();
141             metadata.put(metaSplit[0].trim(),value.trim());
142             processMetadata(iterator);
143         }
144         else {
145             processSourcesAndNonManoSources(iterator, line);
146         }
147     }
148
149     private void processNonManoInputs(Iterator<String> iterator, String prevLine) {
150         //Non Mano input should always start with key, if no key available report an error
151         if(prevLine.trim().equals(SOURCE_MF_ATTRIBUTE + SEPERATOR_MF_ATTRIBUTE)){
152             reportError(prevLine);
153             return;
154         }
155         //key should contain : separator
156         if(!prevLine.contains(SEPERATOR_MF_ATTRIBUTE)){
157             reportError(prevLine);
158             return;
159         }
160         //key shouldn't have value in the same line
161         String[] metaSplit = prevLine.trim().split(SEPERATOR_MF_ATTRIBUTE);
162         if (metaSplit.length > 1){
163             reportError(prevLine);
164             return;
165         }
166         int index = prevLine.indexOf(':');
167         if(index > 0){
168             prevLine = prevLine.substring(0, index);
169         }
170         processNonManoSource(iterator, prevLine, new ArrayList<>());
171
172     }
173
174     private void processNonManoSource(Iterator<String> iterator, String key, List<String> sources) {
175         if(!iterator.hasNext()){
176             return;
177         }
178         String line = iterator.next();
179         if(line.isEmpty()){
180             processNonManoSource(iterator, key, sources);
181         }else if(line.trim().startsWith(SOURCE_MF_ATTRIBUTE + SEPERATOR_MF_ATTRIBUTE)){
182             String value = line.replace(SOURCE_MF_ATTRIBUTE + SEPERATOR_MF_ATTRIBUTE, "").trim();
183             sources.add(value);
184             processNonManoSource(iterator, key, sources);
185         }else {
186             processNonManoInputs(iterator, line);
187         }
188         nonManoSources.put(key.trim(), sources);
189     }
190
191     private void reportError(String line) {
192         errors.add(getErrorWithParameters(Messages.MANIFEST_INVALID_LINE.getErrorMessage(), line));
193     }
194
195     private ImmutableList<String> readAllLines(InputStream is) throws IOException {
196         if(is == null){
197             throw new IOException("Input Stream cannot be null!");
198         }
199         ImmutableList.Builder<String> builder = ImmutableList.<String> builder();
200         try (BufferedReader bufferedReader = new BufferedReader(
201                 new InputStreamReader(is, StandardCharsets.UTF_8.newDecoder()))) {
202             bufferedReader.lines().forEach(builder::add);
203         }
204         return builder.build();
205     }
206
207     public Map<String, String> getMetadata() {
208         if (!isValid()){
209             return Collections.emptyMap();
210         }
211         return ImmutableMap.copyOf(metadata);
212     }
213
214     public List<String> getSources() {
215         if (!isValid()){
216             return Collections.emptyList();
217         }
218         return ImmutableList.copyOf(sources);
219     }
220
221     public List<String> getErrors() {
222         return  ImmutableList.copyOf(errors);
223     }
224
225     public boolean isValid() {
226         return errors.isEmpty();
227     }
228
229     public Map<String, List<String>> getNonManoSources() {
230         if (!isValid()){
231             return Collections.emptyMap();
232         }
233         return ImmutableMap.copyOf(nonManoSources);
234     }
235 }