View Javadoc
1   /*
2    * dpkg - Debian Package library and the Debian Package Maven plugin
3    * (c) Copyright 2016 Gerrit Hohl
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program 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
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18   */
19  package net.sourceforge.javadpkg.impl;
20  
21  import java.io.BufferedReader;
22  import java.io.ByteArrayInputStream;
23  import java.io.IOException;
24  import java.io.InputStreamReader;
25  import java.util.List;
26  
27  import net.sourceforge.javadpkg.Context;
28  import net.sourceforge.javadpkg.GlobalConstants;
29  import net.sourceforge.javadpkg.ParseException;
30  import net.sourceforge.javadpkg.Symbols;
31  import net.sourceforge.javadpkg.SymbolsParser;
32  import net.sourceforge.javadpkg.control.PackageDependency;
33  import net.sourceforge.javadpkg.control.PackageDependencyParser;
34  import net.sourceforge.javadpkg.control.PackageVersion;
35  import net.sourceforge.javadpkg.control.PackageVersionParser;
36  import net.sourceforge.javadpkg.control.impl.PackageDependencyParserImpl;
37  import net.sourceforge.javadpkg.control.impl.PackageNameParserImpl;
38  import net.sourceforge.javadpkg.control.impl.PackageVersionParserImpl;
39  import net.sourceforge.javadpkg.control.impl.PackageVersionRelationOperatorParserImpl;
40  import net.sourceforge.javadpkg.field.Field;
41  import net.sourceforge.javadpkg.field.FieldParser;
42  import net.sourceforge.javadpkg.field.impl.FieldParserImpl;
43  import net.sourceforge.javadpkg.io.DataSource;
44  import net.sourceforge.javadpkg.io.impl.DataStreamSource;
45  
46  
47  /**
48   * <p>
49   * A {@link SymbolsParser} implementation.
50   * </p>
51   *
52   * @author Gerrit Hohl (gerrit-hohl@users.sourceforge.net)
53   * @version <b>1.0</b>, 10.01.2016 by Gerrit Hohl
54   */
55  public class SymbolsParserImpl implements SymbolsParser, GlobalConstants {
56  	
57  	
58  	/** The parser for the package version. */
59  	private PackageVersionParser	packageVersionParser;
60  	/** The parser for the package dependencies. */
61  	private PackageDependencyParser	packageDependencyParser;
62  	/** The parser for the fields. */
63  	private FieldParser				fieldParser;
64  
65  
66  	/**
67  	 * <p>
68  	 * Creates a parser.
69  	 * </p>
70  	 */
71  	public SymbolsParserImpl() {
72  		super();
73  
74  		this.packageVersionParser = new PackageVersionParserImpl();
75  		this.packageDependencyParser = new PackageDependencyParserImpl(new PackageNameParserImpl(),
76  				new PackageVersionRelationOperatorParserImpl(), this.packageVersionParser);
77  		this.fieldParser = new FieldParserImpl(false, false, false);
78  	}
79  	
80  	
81  	@Override
82  	public Symbols parseSymbols(DataSource source, Context context) throws IOException, ParseException {
83  		SymbolsImpl symbols = null;
84  		String line;
85  		boolean contentParsed = false;
86  		SymbolsEntryImpl entry = null;
87  		
88  		
89  		if (source == null)
90  			throw new IllegalArgumentException("Argument source is null.");
91  		if (context == null)
92  			throw new IllegalArgumentException("Argument context is null.");
93  		
94  		symbols = new SymbolsImpl();
95  		try {
96  			try (BufferedReader reader = new BufferedReader(new InputStreamReader(source.getInputStream(), UTF_8_CHARSET))) {
97  				while ((line = reader.readLine()) != null) {
98  					// --- Alternative dependency ---
99  					if (line.startsWith("| ")) {
100 						if (entry == null)
101 							throw new ParseException("Symbol line |" + line + "| of symbols from source |" + source.getName()
102 									+ "| contains an alternative dependency template also no initial line of an entry has been read.");
103 						if (contentParsed)
104 							throw new ParseException("Symbol line |" + line + "| of symbols from source |" + source.getName()
105 									+ "| contains an alternative dependency template also symbols have been already read.");
106 						this.parseAlternativeDependencyTemplate(source, entry, line);
107 					}
108 					// --- Symbol ---
109 					else if (line.startsWith(" ")) {
110 						if (entry == null)
111 							throw new ParseException("Symbol line |" + line + "| of symbols from source |" + source.getName()
112 									+ "| contains a symbol also no initial line of an entry has been read.");
113 						this.parseSymbol(source, context, entry, line);
114 						contentParsed = true;
115 					}
116 					// --- Meta data fields --
117 					else if (line.startsWith("* ")) {
118 						if (entry == null)
119 							throw new ParseException("Symbol line |" + line + "| of symbols from source |" + source.getName()
120 									+ "| contains a meta data field also no initial line of an entry has been read.");
121 						this.parseMetaData(source, context, entry, line);
122 						contentParsed = true;
123 					}
124 					// --- Initial line of an entry ---
125 					else {
126 						entry = this.parseInitialLine(source, context, line);
127 						symbols.addEntry(entry);
128 						contentParsed = false;
129 					}
130 				}
131 			}
132 		} catch (IOException e) {
133 			throw new IOException(
134 					"Couldn't read the shared libraries from source |" + source.getName() + "|: " + e.getMessage());
135 		}
136 		if (entry == null)
137 			throw new ParseException("The shared libraries from source |" + source.getName() + "| were empty.");
138 		return symbols;
139 	}
140 	
141 	
142 	/**
143 	 * <p>
144 	 * Parses the initial line.
145 	 * </p>
146 	 *
147 	 * @param source
148 	 *            The source.
149 	 * @param context
150 	 *            The context.
151 	 * @param line
152 	 *            The initial line.
153 	 * @return The symbols.
154 	 * @throws ParseException
155 	 *             If an error occurs during parsing.
156 	 */
157 	private SymbolsEntryImpl parseInitialLine(DataSource source, Context context, String line) throws ParseException {
158 		SymbolsEntryImpl entry;
159 		String[] parts;
160 		String libraryName, dependencyTemplate;
161 		PackageDependency dependency = null;
162 		
163 		
164 		parts = line.split(" ", 2);
165 		if (parts.length != 2)
166 			throw new ParseException("Couldn't split line |" + line + "| of symbols from source |" + source.getName()
167 					+ "| into 3 parts. Got " + parts.length);
168 		libraryName = parts[0];
169 		dependencyTemplate = parts[1];
170 		if (!dependencyTemplate.endsWith("#MINVER#")) {
171 			try {
172 				dependency = this.packageDependencyParser.parsePackageDependency(dependencyTemplate, context);
173 			} catch (ParseException e) {
174 				throw new ParseException("Couldn't parse dependency |" + dependencyTemplate + "| from |" + line
175 						+ "| of symbols from source |" + source.getName() + "| into 3 parts. Got " + parts.length);
176 			}
177 		}
178 		
179 		if (dependency == null) {
180 			entry = new SymbolsEntryImpl(libraryName, dependencyTemplate);
181 		} else {
182 			entry = new SymbolsEntryImpl(libraryName, dependency);
183 		}
184 		return entry;
185 	}
186 
187 
188 	/**
189 	 * <p>
190 	 * Parses the alternative dependency template.
191 	 * </p>
192 	 *
193 	 * @param source
194 	 *            The source.
195 	 * @param entry
196 	 *            The entry.
197 	 * @param line
198 	 *            The line.
199 	 * @throws ParseException
200 	 *             If an error occurs during parsing.
201 	 */
202 	private void parseAlternativeDependencyTemplate(DataSource source, SymbolsEntryImpl entry, String line)
203 			throws ParseException {
204 		
205 		String alternativeDependencyTemplate;
206 
207 		// TODO Implement the parsing of the dependencies.
208 		alternativeDependencyTemplate = line.substring(2);
209 		entry.setAlternativeDependencyValue(alternativeDependencyTemplate);
210 	}
211 	
212 	
213 	/**
214 	 * <p>
215 	 * Parses a symbol.
216 	 * </p>
217 	 *
218 	 * @param source
219 	 *            The source.
220 	 * @param context
221 	 *            The context.
222 	 * @param entry
223 	 *            The entry.
224 	 * @param line
225 	 *            The line.
226 	 * @throws ParseException
227 	 *             If an error occurs during parsing.
228 	 */
229 	private void parseSymbol(DataSource source, Context context, SymbolsEntryImpl entry, String line) throws ParseException {
230 		String[] parts;
231 		PackageVersion version;
232 		String symbolName, symbolVersion;
233 
234 
235 		parts = line.trim().split(" ", 3);
236 		if ((parts.length < 2) || (parts.length > 3))
237 			throw new ParseException("Couldn't split line |" + line + "| of symbols from source |" + source.getName()
238 					+ "| into 2 or 3 parts. Got " + parts.length);
239 		
240 		try {
241 			version = this.packageVersionParser.parsePackageVersion(parts[1], context);
242 		} catch (ParseException e) {
243 			throw new ParseException("Couldn't parse version |" + parts[1] + "| from |" + line + "| of symbols from source |"
244 					+ source.getName() + "|: " + e.getMessage(), e);
245 		}
246 		parts = parts[0].split("@", 2);
247 		if (parts.length != 2)
248 			throw new ParseException("Couldn't split |" + line + "| of symbols from source |" + source.getName()
249 					+ "| into 2 parts. Got " + parts.length);
250 		symbolName = parts[0];
251 		symbolVersion = parts[1];
252 		
253 		entry.addSymbol(symbolName, symbolVersion, version);
254 	}
255 	
256 	
257 	/**
258 	 * <p>
259 	 * Parses a meta data field.
260 	 * </p>
261 	 *
262 	 * @param source
263 	 *            The source.
264 	 * @param context
265 	 *            The context.
266 	 * @param entry
267 	 *            The entry.
268 	 * @param line
269 	 *            The line.
270 	 * @throws ParseException
271 	 *             If an error occurs during parsing.
272 	 */
273 	private void parseMetaData(DataSource source, Context context, SymbolsEntryImpl entry, String line) throws ParseException {
274 		String trimmedLine;
275 		List<Field> fields;
276 		Field field;
277 		PackageDependency dependency;
278 		
279 		
280 		trimmedLine = line.substring(2);
281 		try {
282 			try (DataSource fieldSource = new DataStreamSource(new ByteArrayInputStream(trimmedLine.getBytes()), "field",
283 					false)) {
284 				fields = this.fieldParser.parseFieldsAsList(fieldSource, context);
285 			}
286 		} catch (IOException | ParseException e) {
287 			throw new ParseException("Couldn't parse meta data field in line |" + line + "| of symbols from source |"
288 					+ source.getName() + "|: " + e.getMessage(), e);
289 		}
290 		if (fields.isEmpty())
291 			throw new ParseException("Couldn't parse meta data field in line |" + line + "| of symbols from source |"
292 					+ source.getName() + "|: Didn't find any field.");
293 		field = fields.get(0);
294 		switch (field.getName().toLowerCase()) {
295 			case "build-depends-package":
296 				// TODO Check if the field is already set in the entry.
297 				try {
298 					dependency = this.packageDependencyParser.parsePackageDependency(field.getValue(), context);
299 				} catch (ParseException e) {
300 					throw new ParseException("Couldn't parse meta data field |" + field.getName() + "| in line |" + line
301 							+ "| of symbols from source |" + source.getName() + "|: " + e.getMessage(), e);
302 				}
303 				entry.setBuildDependsPackage(dependency);
304 				break;
305 			
306 			default:
307 				throw new ParseException("Found unsupported meta data field |" + field.getName() + "| in line |" + line
308 						+ "| of symbols from source |" + source.getName() + "|.");
309 		}
310 	}
311 	
312 	
313 }