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.field.impl;
20  
21  import java.io.BufferedReader;
22  import java.io.IOException;
23  import java.io.InputStreamReader;
24  import java.util.ArrayList;
25  import java.util.LinkedHashMap;
26  import java.util.List;
27  import java.util.Map;
28  
29  import net.sourceforge.javadpkg.Context;
30  import net.sourceforge.javadpkg.GlobalConstants;
31  import net.sourceforge.javadpkg.ParseException;
32  import net.sourceforge.javadpkg.field.Field;
33  import net.sourceforge.javadpkg.field.FieldParser;
34  import net.sourceforge.javadpkg.io.DataSource;
35  
36  
37  /**
38   * <p>
39   * A {@link FieldParser} implementation.
40   * </p>
41   *
42   * @author Gerrit Hohl (gerrit-hohl@users.sourceforge.net)
43   * @version <b>1.0</b>, 03.01.2016 by Gerrit Hohl
44   */
45  public class FieldParserImpl implements FieldParser, GlobalConstants {
46  	
47  	
48  	/** The flag if empty lines should not throw a {@link ParseException}. */
49  	private boolean	allowEmptyLines;
50  	/**
51  	 * <p>
52  	 * The flag if empty lines should be returned by the
53  	 * {@link #parseFieldsAsList(DataSource, Context)} method.
54  	 * </p>
55  	 */
56  	private boolean	returnEmptyLines;
57  	/**
58  	 * The flag if nameless fields should not throw a {@link ParseException}.
59  	 */
60  	private boolean	allowNamelessFields;
61  	
62  	
63  	/**
64  	 * <p>
65  	 * Creates a parser.
66  	 * </p>
67  	 *
68  	 * @param allowEmptyLines
69  	 *            The flag if empty lines are allowed. Otherwise a
70  	 *            {@link ParseException} is thrown when an empty line is found.
71  	 * @param returnEmptyLines
72  	 *            The flag if empty lines should be returned by the
73  	 *            {@link #parseFieldsAsList(DataSource, Context)} method.
74  	 * @param allowNamelessFields
75  	 *            The flag if nameless fields should not throw a
76  	 *            {@link ParseException}.
77  	 */
78  	public FieldParserImpl(boolean allowEmptyLines, boolean returnEmptyLines, boolean allowNamelessFields) {
79  		super();
80  		
81  		this.allowEmptyLines = allowEmptyLines;
82  		this.returnEmptyLines = returnEmptyLines;
83  		this.allowNamelessFields = allowNamelessFields;
84  	}
85  
86  
87  	@Override
88  	public Map<String, Field> parseFieldsAsMap(DataSource source, Context context) throws IOException, ParseException {
89  		Map<String, Field> map;
90  		List<Field> list;
91  		String name;
92  
93  
94  		if (source == null)
95  			throw new IllegalArgumentException("Argument source is null.");
96  		if (context == null)
97  			throw new IllegalArgumentException("Argument context is null.");
98  		
99  		// --- Get the fields ---
100 		list = this.parseFieldsAsList(source, context);
101 		
102 		// --- Create a map ---
103 		map = new LinkedHashMap<>();
104 		for (Field field : list) {
105 			// --- Ignore empty fields and nameless fields ---
106 			if (field.isEmpty() || field.isNameless()) {
107 				continue;
108 			}
109 
110 			name = field.getName().toLowerCase();
111 			if (map.containsKey(name))
112 				throw new ParseException("The field |" + field.getName() + "| exists more than one time in the source |"
113 						+ source.getName() + "|.");
114 			map.put(name, field);
115 		}
116 		return map;
117 	}
118 
119 
120 	@Override
121 	public List<Field> parseFieldsAsList(DataSource source, Context context) throws IOException, ParseException {
122 		List<Field> fields;
123 		String line, name, value;
124 		FieldImpl field = null;
125 		int index;
126 		boolean lastLine = false;
127 
128 
129 		if (source == null)
130 			throw new IllegalArgumentException("Argument source is null.");
131 		if (context == null)
132 			throw new IllegalArgumentException("Argument context is null.");
133 		
134 		// --- Parse the fields ---
135 		fields = new ArrayList<>();
136 		try {
137 			try (BufferedReader reader = new BufferedReader(new InputStreamReader(source.getInputStream(), UTF_8_CHARSET))) {
138 				while ((line = reader.readLine()) != null) {
139 					// --- Did we already read the last line? ---
140 					if (lastLine)
141 						throw new ParseException(
142 								"Found a line |" + line + "| in source |" + source.getName() + "| which is not a field.");
143 					
144 					// --- A comment? ---
145 					if (line.startsWith("#")) {
146 						continue;
147 					}
148 					// --- A continuation? ---
149 					else if (line.startsWith(" ") || line.startsWith("\t")) {
150 						if (field == null)
151 							if (this.allowNamelessFields) {
152 								fields.add(new NamelessField(line));
153 							} else
154 								throw new ParseException("Found a continuation line |" + line + "| in source |"
155 										+ source.getName() + "|, but the initial line of the field is missing.");
156 						else {
157 							field.addValue("\n");
158 							line = line.substring(1);
159 							field.addValue(line);
160 						}
161 					}
162 					// --- Otherwise it must be a new field or an empty line ---
163 					else {
164 						// --- Do we have an empty line ---
165 						if (line.isEmpty()) {
166 							// --- If we don't allow empty lines than an empty line must be the last line ---
167 							if (!this.allowEmptyLines) {
168 								lastLine = true;
169 							}
170 							// --- If empty lines should be return we add one ---
171 							else if (this.returnEmptyLines) {
172 								fields.add(EmptyField.EMPTY_FIELD);
173 							}
174 						}
175 						// --- Otherwise we should have a new field ---
176 						else {
177 							index = line.indexOf(':');
178 							if (index == -1) {
179 								if (this.allowNamelessFields) {
180 									fields.add(new NamelessField(line));
181 								} else
182 									throw new ParseException("Found a line |" + line + "| in source |" + source.getName()
183 											+ "| which is not a field.");
184 							} else {
185 								name = line.substring(0, index).trim();
186 								value = line.substring(index + 1).trim();
187 								field = new FieldImpl(name, value);
188 								fields.add(field);
189 							}
190 						}
191 					}
192 				}
193 			}
194 		} catch (IOException e) {
195 			throw new IOException(
196 					"Couldn't read control for Debian binary package from source |" + source.getName() + "|: " + e.getMessage(),
197 					e);
198 		}
199 		return fields;
200 	}
201 
202 
203 }