8858128b385eb97f947915f1893c04b4dbb9560c
[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             return;
137         }
138         if (!metaSplit[0].equals(SOURCE_MF_ATTRIBUTE) && !metaSplit[0].equals(NON_MANO_MF_ATTRIBUTE)){
139             String value = line.substring((metaSplit[0] + SEPERATOR_MF_ATTRIBUTE).length()).trim();
140             metadata.put(metaSplit[0],value);
141             processMetadata(iterator);
142         }
143         else {
144             processSourcesAndNonManoSources(iterator, line);
145         }
146     }
147
148     private void processNonManoInputs(Iterator<String> iterator, String prevLine) {
149         //Non Mano input should always start with key, if no key available report an error
150         if(prevLine.trim().equals(SOURCE_MF_ATTRIBUTE + SEPERATOR_MF_ATTRIBUTE)){
151             reportError(prevLine);
152             return;
153         }
154         //key should contain : separator
155         if(!prevLine.contains(SEPERATOR_MF_ATTRIBUTE)){
156             reportError(prevLine);
157             return;
158         }
159         //key shouldn't have value in the same line
160         String[] metaSplit = prevLine.trim().split(SEPERATOR_MF_ATTRIBUTE);
161         if (metaSplit.length > 1){
162             reportError(prevLine);
163             return;
164         }
165         int index = prevLine.indexOf(':');
166         if(index > 0){
167             prevLine = prevLine.substring(0, index);
168         }
169         processNonManoSource(iterator, prevLine, new ArrayList<>());
170
171     }
172
173     private void processNonManoSource(Iterator<String> iterator, String key, List<String> sources) {
174         if(!iterator.hasNext()){
175             return;
176         }
177         String line = iterator.next();
178         if(line.isEmpty()){
179             processNonManoSource(iterator, key, sources);
180         }else if(line.trim().startsWith(SOURCE_MF_ATTRIBUTE + SEPERATOR_MF_ATTRIBUTE)){
181             String value = line.replace(SOURCE_MF_ATTRIBUTE + SEPERATOR_MF_ATTRIBUTE, "").trim();
182             sources.add(value);
183             processNonManoSource(iterator, key, sources);
184         }else {
185             processNonManoInputs(iterator, line);
186         }
187         nonManoSources.put(key.trim(), sources);
188     }
189
190     private void reportError(String line) {
191         errors.add(getErrorWithParameters(Messages.MANIFEST_INVALID_LINE.getErrorMessage(), line));
192     }
193
194     private ImmutableList<String> readAllLines(InputStream is) throws IOException {
195         if(is == null){
196             throw new IOException("Input Stream cannot be null!");
197         }
198         ImmutableList.Builder<String> builder = ImmutableList.<String> builder();
199         try (BufferedReader bufferedReader = new BufferedReader(
200                 new InputStreamReader(is, StandardCharsets.UTF_8.newDecoder()))) {
201             bufferedReader.lines().forEach(builder::add);
202         }
203         return builder.build();
204     }
205
206     public Map<String, String> getMetadata() {
207         if (!isValid()){
208             return Collections.emptyMap();
209         }
210         return ImmutableMap.copyOf(metadata);
211     }
212
213     public List<String> getSources() {
214         if (!isValid()){
215             return Collections.emptyList();
216         }
217         return ImmutableList.copyOf(sources);
218     }
219
220     public List<String> getErrors() {
221         return  ImmutableList.copyOf(errors);
222     }
223
224     public boolean isValid() {
225         return errors.isEmpty();
226     }
227
228     public Map<String, List<String>> getNonManoSources() {
229         if (!isValid()){
230             return Collections.emptyMap();
231         }
232         return ImmutableMap.copyOf(nonManoSources);
233     }
234 }