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.control.impl;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import net.sourceforge.javadpkg.BuildException;
26  import net.sourceforge.javadpkg.Context;
27  import net.sourceforge.javadpkg.control.Architecture;
28  import net.sourceforge.javadpkg.control.ArchitectureBuilder;
29  import net.sourceforge.javadpkg.control.BinaryControl;
30  import net.sourceforge.javadpkg.control.Control;
31  import net.sourceforge.javadpkg.control.ControlBuilder;
32  import net.sourceforge.javadpkg.control.ControlConstants;
33  import net.sourceforge.javadpkg.control.Description;
34  import net.sourceforge.javadpkg.control.DescriptionBuilder;
35  import net.sourceforge.javadpkg.control.EssentialBuilder;
36  import net.sourceforge.javadpkg.control.Homepage;
37  import net.sourceforge.javadpkg.control.HomepageBuilder;
38  import net.sourceforge.javadpkg.control.ModuleAliasesBuilder;
39  import net.sourceforge.javadpkg.control.PackageDependency;
40  import net.sourceforge.javadpkg.control.PackageDependencyBuilder;
41  import net.sourceforge.javadpkg.control.PackageMaintainer;
42  import net.sourceforge.javadpkg.control.PackageMaintainerBuilder;
43  import net.sourceforge.javadpkg.control.PackageMultiArchitecture;
44  import net.sourceforge.javadpkg.control.PackageMultiArchitectureBuilder;
45  import net.sourceforge.javadpkg.control.PackageName;
46  import net.sourceforge.javadpkg.control.PackageNameBuilder;
47  import net.sourceforge.javadpkg.control.PackagePriority;
48  import net.sourceforge.javadpkg.control.PackagePriorityBuilder;
49  import net.sourceforge.javadpkg.control.PackageVersion;
50  import net.sourceforge.javadpkg.control.PackageVersionBuilder;
51  import net.sourceforge.javadpkg.control.Section;
52  import net.sourceforge.javadpkg.control.SectionBuilder;
53  import net.sourceforge.javadpkg.control.Size;
54  import net.sourceforge.javadpkg.control.SizeBuilder;
55  import net.sourceforge.javadpkg.field.Field;
56  import net.sourceforge.javadpkg.field.FieldBuilder;
57  import net.sourceforge.javadpkg.field.FieldType;
58  import net.sourceforge.javadpkg.field.impl.FieldBuilderImpl;
59  import net.sourceforge.javadpkg.field.impl.FieldImpl;
60  import net.sourceforge.javadpkg.io.DataTarget;
61  
62  
63  /**
64   * <p>
65   * A {@link ControlBuilder} implementation.
66   * </p>
67   *
68   * @author Gerrit Hohl (gerrit-hohl@users.sourceforge.net)
69   * @version <b>1.0</b>, 26.04.2016 by Gerrit Hohl
70   */
71  public class ControlBuilderImpl implements ControlBuilder, ControlConstants {
72  	
73  	
74  	/** The builder for the package name. */
75  	private PackageNameBuilder				packageNameBuilder;
76  	/** The builder for the package dependencies. */
77  	private PackageDependencyBuilder		packageDependencyBuilder;
78  	/** The builder for the package version. */
79  	private PackageVersionBuilder			packageVersionBuilder;
80  	/** The builder for the architecture. */
81  	private ArchitectureBuilder				architectureBuilder;
82  	/** The builder for the section. */
83  	private SectionBuilder					sectionBuilder;
84  	/** The builder for the package priority. */
85  	private PackagePriorityBuilder			packagePriorityBuilder;
86  	/** The builder for the essential flag. */
87  	private EssentialBuilder				essentialBuilder;
88  	/** The builder for the size. */
89  	private SizeBuilder						sizeBuilder;
90  	/** The builder for the package maintainer. */
91  	private PackageMaintainerBuilder		packageMaintainerBuilder;
92  	/** The builder for the description. */
93  	private DescriptionBuilder				descriptionBuilder;
94  	/** The builder for the home-page. */
95  	private HomepageBuilder					homepageBuilder;
96  	/** The builder for the multiple architecture. */
97  	private PackageMultiArchitectureBuilder	packageMultiArchitectureBuilder;
98  	/** The builder for the module aliases. */
99  	private ModuleAliasesBuilder			moduleAliasesBuilder;
100 	/** The builder for the fields. */
101 	private FieldBuilder					fieldBuilder;
102 	
103 	
104 	/**
105 	 * <p>
106 	 * Creates a builder.
107 	 * </p>
108 	 */
109 	public ControlBuilderImpl() {
110 		super();
111 
112 		this.packageNameBuilder = new PackageNameBuilderImpl();
113 		this.packageVersionBuilder = new PackageVersionBuilderImpl();
114 		this.packageDependencyBuilder = new PackageDependencyBuilderImpl(this.packageNameBuilder,
115 				new PackageVersionRelationOperatorBuilderImpl(), this.packageVersionBuilder);
116 		this.architectureBuilder = new ArchitectureBuilderImpl();
117 		this.sectionBuilder = new SectionBuilderImpl();
118 		this.packagePriorityBuilder = new PackagePriorityBuilderImpl();
119 		this.essentialBuilder = new EssentialBuilderImpl();
120 		this.sizeBuilder = new SizeBuilderImpl();
121 		this.packageMaintainerBuilder = new PackageMaintainerBuilderImpl();
122 		this.descriptionBuilder = new DescriptionBuilderImpl();
123 		this.homepageBuilder = new HomepageBuilderImpl();
124 		this.packageMultiArchitectureBuilder = new PackageMultiArchitectureBuilderImpl();
125 		this.moduleAliasesBuilder = new ModuleAliasesBuilderImpl();
126 		this.fieldBuilder = new FieldBuilderImpl();
127 	}
128 	
129 	
130 	@Override
131 	public void buildControl(Control control, DataTarget target, Context context) throws IOException, BuildException {
132 		Size installedSize;
133 		
134 		
135 		if (control == null)
136 			throw new IllegalArgumentException("Argument control is null.");
137 		if (target == null)
138 			throw new IllegalArgumentException("Argument target is null.");
139 		if (context == null)
140 			throw new IllegalArgumentException("Argument context is null.");
141 		
142 		if (control instanceof BinaryControl) {
143 			installedSize = ((BinaryControl) control).getInstalledSize();
144 			if (installedSize == null)
145 				throw new IllegalArgumentException("Property installedSize of argument control is null.");
146 		} else
147 			throw new BuildException("Can't build control of type |" + control.getClass().getCanonicalName()
148 					+ "| as currently only control of type |" + BinaryControl.class.getCanonicalName() + "| is supported.");
149 		
150 		this.buildControl(control, installedSize, target, context);
151 	}
152 
153 
154 	@Override
155 	public void buildControl(Control control, Size installedSize, DataTarget target, Context context)
156 			throws IOException, BuildException {
157 		
158 		List<Field> fields;
159 		
160 		
161 		if (control == null)
162 			throw new IllegalArgumentException("Argument control is null.");
163 		if (installedSize == null)
164 			throw new IllegalArgumentException("Argument installedSize is null.");
165 		if (target == null)
166 			throw new IllegalArgumentException("Argument target is null.");
167 		if (context == null)
168 			throw new IllegalArgumentException("Argument context is null.");
169 		if (installedSize.getBytes() < 0)
170 			throw new IllegalArgumentException("Argument installedSize is negative: " + installedSize.getBytes());
171 		
172 		// --- Build the fields ---
173 		if (control instanceof BinaryControl) {
174 			fields = this.buildBinaryControl((BinaryControl) control, installedSize, context);
175 		} else
176 			throw new BuildException("Can't build control of type |" + control.getClass().getCanonicalName()
177 					+ "| as currently only control of type |" + BinaryControl.class.getCanonicalName() + "| is supported.");
178 		
179 		// --- Write the fields ---
180 		try {
181 			this.fieldBuilder.buildFields(fields, target, context);
182 		} catch (BuildException e) {
183 			throw new BuildException("Couldn't write control file: " + e.getMessage(), e);
184 		} catch (IOException e) {
185 			throw new IOException("Couldn't write control file: " + e.getMessage(), e);
186 		}
187 	}
188 
189 
190 	/**
191 	 * <p>
192 	 * Builds the list of fields for a binary control.
193 	 * </p>
194 	 *
195 	 * @param control
196 	 *            The binary control.
197 	 * @param installedSize
198 	 *            The installed size.
199 	 * @param context
200 	 *            The context.
201 	 * @return The fields.
202 	 * @throws BuildException
203 	 *             If an error occurs during the building.
204 	 */
205 	private List<Field> buildBinaryControl(BinaryControl control, Size installedSize, Context context) throws BuildException {
206 		
207 		List<Field> fields;
208 		List<PackageDependency> dependencies;
209 
210 
211 		fields = new ArrayList<>();
212 
213 		// --- Package (mandatory) ---
214 		this.addField(context, control, fields, FIELD_PACKAGE, control.getPackage(), FieldType.MANDATORY,
215 				new FieldValueBuilder<PackageName, PackageNameBuilder>(this.packageNameBuilder) {
216 					
217 					
218 					@Override
219 					public String build(PackageNameBuilder builder, PackageName value, Context context) throws BuildException {
220 						return builder.buildPackageName(value, context);
221 					}
222 				});
223 		
224 		// --- Source ---
225 		this.addField(context, control, fields, FIELD_SOURCE, control.getSource(), FieldType.OPTIONAL,
226 				new FieldValueBuilder<PackageDependency, PackageDependencyBuilder>(this.packageDependencyBuilder) {
227 					
228 					
229 					@Override
230 					public String build(PackageDependencyBuilder builder, PackageDependency value, Context context)
231 							throws BuildException {
232 						
233 						return builder.buildPackageDependency(value, context);
234 					}
235 				});
236 		
237 		// --- Version (mandatory) ---
238 		this.addField(context, control, fields, FIELD_VERSION, control.getVersion(), FieldType.MANDATORY,
239 				new FieldValueBuilder<PackageVersion, PackageVersionBuilder>(this.packageVersionBuilder) {
240 					
241 					
242 					@Override
243 					public String build(PackageVersionBuilder builder, PackageVersion value, Context context)
244 							throws BuildException {
245 						
246 						return builder.buildPackageVersion(value, context);
247 					}
248 				});
249 		
250 		// --- Architecture (mandatory) ---
251 		this.addField(context, control, fields, FIELD_ARCHITECTURE, control.getArchitecture(), FieldType.MANDATORY,
252 				new FieldValueBuilder<Architecture, ArchitectureBuilder>(this.architectureBuilder) {
253 					
254 					
255 					@Override
256 					public String build(ArchitectureBuilder builder, Architecture value, Context context)
257 							throws BuildException {
258 						
259 						return builder.buildArchitecture(value, context);
260 					}
261 				});
262 		
263 		// --- Section (recommended) ---
264 		this.addField(context, control, fields, FIELD_SECTION, control.getSection(), FieldType.RECOMMENDED,
265 				new FieldValueBuilder<Section, SectionBuilder>(this.sectionBuilder) {
266 					
267 					
268 					@Override
269 					public String build(SectionBuilder builder, Section value, Context context) throws BuildException {
270 						return builder.buildSection(value, context);
271 					}
272 				});
273 		
274 		// --- Priority (recommended) ---
275 		this.addField(context, control, fields, FIELD_PRIORITY, control.getPriority(), FieldType.RECOMMENDED,
276 				new FieldValueBuilder<PackagePriority, PackagePriorityBuilder>(this.packagePriorityBuilder) {
277 					
278 					
279 					@Override
280 					public String build(PackagePriorityBuilder builder, PackagePriority value, Context context)
281 							throws BuildException {
282 						
283 						return builder.buildPackagePriority(value, context);
284 					}
285 				});
286 		
287 		// --- Essential ---
288 		this.addField(context, control, fields, FIELD_ESSENTIAL, control.getEssential(), FieldType.OPTIONAL,
289 				new FieldValueBuilder<Boolean, EssentialBuilder>(this.essentialBuilder) {
290 					
291 					
292 					@Override
293 					public String build(EssentialBuilder builder, Boolean value, Context context) throws BuildException {
294 						
295 						return builder.buildEssential(value, context);
296 					}
297 				});
298 		
299 		// --- Depends ---
300 		dependencies = control.getDepends();
301 		this.addDependenciesField(context, control, fields, FIELD_DEPENDS, dependencies, FieldType.OPTIONAL);
302 
303 		// --- Recommends ---
304 		dependencies = control.getRecommends();
305 		this.addDependenciesField(context, control, fields, FIELD_RECOMMENDS, dependencies, FieldType.OPTIONAL);
306 
307 		// --- Suggests ---
308 		dependencies = control.getSuggests();
309 		this.addDependenciesField(context, control, fields, FIELD_SUGGESTS, dependencies, FieldType.OPTIONAL);
310 
311 		// --- Enhances ---
312 		dependencies = control.getEnhances();
313 		this.addDependenciesField(context, control, fields, FIELD_ENHANCES, dependencies, FieldType.OPTIONAL);
314 
315 		// --- Pre-Depends ---
316 		dependencies = control.getPreDepends();
317 		this.addDependenciesField(context, control, fields, FIELD_PRE_DEPENDS, dependencies, FieldType.OPTIONAL);
318 
319 		// --- Breaks ---
320 		dependencies = control.getBreaks();
321 		this.addDependenciesField(context, control, fields, FIELD_BREAKS, dependencies, FieldType.OPTIONAL);
322 
323 		// --- Conflicts ---
324 		dependencies = control.getConflicts();
325 		this.addDependenciesField(context, control, fields, FIELD_CONFLICTS, dependencies, FieldType.OPTIONAL);
326 
327 		// --- Installed-Size ---
328 		this.addField(context, control, fields, FIELD_INSTALLED_SIZE, installedSize, FieldType.OPTIONAL,
329 				new FieldValueBuilder<Size, SizeBuilder>(this.sizeBuilder) {
330 					
331 					
332 					@Override
333 					public String build(SizeBuilder builder, Size value, Context context) throws BuildException {
334 						
335 						return builder.buildSizeInKiloBytes(value, context);
336 					}
337 				});
338 		
339 		// --- Maintainer (mandatory) ---
340 		this.addMaintainerField(context, control, fields, FIELD_MAINTAINER, control.getMaintainer(), FieldType.MANDATORY);
341 		
342 		// --- Homepage ---
343 		this.addField(context, control, fields, FIELD_HOMEPAGE, control.getHomepage(), FieldType.OPTIONAL,
344 				new FieldValueBuilder<Homepage, HomepageBuilder>(this.homepageBuilder) {
345 					
346 					
347 					@Override
348 					public String build(HomepageBuilder builder, Homepage value, Context context) throws BuildException {
349 						
350 						return builder.buildHomepage(value, context);
351 					}
352 				});
353 		
354 		// --- Built-Using ---
355 		this.addDependenciesField(context, control, fields, FIELD_BUILT_USING, control.getBuiltUsing(), FieldType.OPTIONAL);
356 
357 		// --- Package-Type ---
358 		// TODO Implement Package-Type field.
359 
360 		// --- Multi-Architecture (not supported yet) ---
361 		this.addField(context, control, fields, FIELD_MULTI_ARCH, control.getMultiArchitecture(), FieldType.OPTIONAL,
362 				new FieldValueBuilder<PackageMultiArchitecture, PackageMultiArchitectureBuilder>(
363 						this.packageMultiArchitectureBuilder) {
364 					
365 					
366 					@Override
367 					public String build(PackageMultiArchitectureBuilder builder, PackageMultiArchitecture value,
368 							Context context) throws BuildException {
369 						
370 						return builder.buildPackageMultiArchitecture(value, context);
371 					}
372 				});
373 		
374 		// --- Original-Maintainer ---
375 		this.addMaintainerField(context, control, fields, FIELD_ORIGINAL_MAINTAINER, control.getOriginalMaintainer(),
376 				FieldType.OPTIONAL);
377 		
378 		// --- Provides ---
379 		this.addDependenciesField(context, control, fields, FIELD_PROVIDES, control.getProvides(), FieldType.OPTIONAL);
380 
381 		// --- Modaliases ---
382 		this.addField(context, control, fields, FIELD_MODALIASES, control.getModuleAliases(), FieldType.OPTIONAL,
383 				new FieldValueBuilder<String, ModuleAliasesBuilder>(this.moduleAliasesBuilder) {
384 					
385 					
386 					@Override
387 					public String build(ModuleAliasesBuilder builder, String value, Context context) throws BuildException {
388 						
389 						return builder.buildModuleAliases(value, context);
390 					}
391 				});
392 		
393 		// --- Description (mandatory) ---
394 		this.addField(context, control, fields, FIELD_DESCRIPTION, control.getDescription(), FieldType.MANDATORY,
395 				new FieldValueBuilder<Description, DescriptionBuilder>(this.descriptionBuilder) {
396 					
397 					
398 					@Override
399 					public String build(DescriptionBuilder builder, Description value, Context context) throws BuildException {
400 						
401 						return builder.buildDescription(value, context);
402 					}
403 				});
404 		
405 		return fields;
406 	}
407 
408 
409 	/**
410 	 * <p>
411 	 * Asserts that the specified field matches the specified type.
412 	 * </p>
413 	 *
414 	 * @param context
415 	 *            The context.
416 	 * @param name
417 	 *            The field name.
418 	 * @param value
419 	 *            The field value.
420 	 * @param type
421 	 *            The type.
422 	 * @throws BuildException
423 	 *             If the field is mandatory, but no value is available.
424 	 */
425 	private void assertField(Context context, String name, Object value, FieldType type) throws BuildException {
426 		if (value == null) {
427 			switch (type) {
428 				case MANDATORY:
429 					throw new BuildException("Mandatory field |" + name + "| is not set.");
430 					
431 				case RECOMMENDED:
432 					context.addWarning(new ControlRecommendedFieldNotSetWarning(name));
433 					break;
434 				
435 				case OPTIONAL:
436 					break;
437 				
438 				default:
439 					throw new BuildException("Unsupported field type |" + type + "|.");
440 			}
441 		}
442 	}
443 
444 
445 	/**
446 	 * <p>
447 	 * Adds a field to the list.
448 	 * </p>
449 	 *
450 	 * @param <T>
451 	 *            The type of the field value.
452 	 * @param <B>
453 	 *            The type of the builder for the field value type.
454 	 * @param context
455 	 *            The context.
456 	 * @param control
457 	 *            The control.
458 	 * @param fields
459 	 *            The list of fields.
460 	 * @param name
461 	 *            The name of the field.
462 	 * @param value
463 	 *            The value of the field.
464 	 * @param type
465 	 *            The type of the field.
466 	 * @param builder
467 	 *            The builder for the field value type.
468 	 * @throws BuildException
469 	 *             If an error occurs during building.
470 	 */
471 	private <T, B> void addField(Context context, BinaryControl control, List<Field> fields, String name, T value,
472 			FieldType type, FieldValueBuilder<T, B> builder) throws BuildException {
473 		
474 		String fieldValue;
475 		
476 		
477 		// --- Make sure mandatory and recommended fields are handled correctly ---
478 		this.assertField(context, name, value, type);
479 		
480 		// --- Handle the value (if available) ---
481 		if (value != null) {
482 			try {
483 				fieldValue = builder.build(value, context);
484 			} catch (BuildException e) {
485 				throw new BuildException("Couldn't build field |" + name + "|: ", e);
486 			}
487 			if (!fieldValue.isEmpty()) {
488 				fields.add(new FieldImpl(name, fieldValue));
489 			}
490 		}
491 	}
492 
493 
494 	/**
495 	 * <p>
496 	 * Adds a dependencies field.
497 	 * </p>
498 	 *
499 	 * @param context
500 	 *            The context.
501 	 * @param control
502 	 *            The control.
503 	 * @param fields
504 	 *            The fields.
505 	 * @param name
506 	 *            The name of the field.
507 	 * @param dependencies
508 	 *            The dependencies.
509 	 * @param type
510 	 *            The type.
511 	 * @throws BuildException
512 	 *             If an error occurs during the build.
513 	 */
514 	private void addDependenciesField(Context context, BinaryControl control, List<Field> fields, String name,
515 			List<PackageDependency> dependencies, FieldType type) throws BuildException {
516 		
517 		this.addField(context, control, fields, name, dependencies, type,
518 				new FieldValueBuilder<List<PackageDependency>, PackageDependencyBuilder>(this.packageDependencyBuilder) {
519 					
520 					
521 					@Override
522 					public String build(PackageDependencyBuilder builder, List<PackageDependency> value, Context context)
523 							throws BuildException {
524 						
525 						return builder.buildPackageDependencies(value, context);
526 					}
527 				});
528 	}
529 
530 
531 	/**
532 	 * <p>
533 	 * Adds a maintainer field.
534 	 * </p>
535 	 *
536 	 * @param context
537 	 *            The context.
538 	 * @param control
539 	 *            The control.
540 	 * @param fields
541 	 *            The fields.
542 	 * @param name
543 	 *            The name of the field.
544 	 * @param maintainer
545 	 *            The maintainer.
546 	 * @param type
547 	 *            The type.
548 	 * @throws BuildException
549 	 *             If an error occurs during the build.
550 	 */
551 	private void addMaintainerField(Context context, BinaryControl control, List<Field> fields, String name,
552 			PackageMaintainer maintainer, FieldType type) throws BuildException {
553 		
554 		this.addField(context, control, fields, name, maintainer, type,
555 				new FieldValueBuilder<PackageMaintainer, PackageMaintainerBuilder>(this.packageMaintainerBuilder) {
556 					
557 					
558 					@Override
559 					public String build(PackageMaintainerBuilder builder, PackageMaintainer value, Context context)
560 							throws BuildException {
561 						
562 						return builder.buildPackageMaintainer(value, context);
563 					}
564 				});
565 	}
566 	
567 	
568 	/* **********************************************************************
569 	 * **********************************************************************
570 	 * **********************************************************************
571 	 * **********************************************************************
572 	 * **********************************************************************
573 	 */
574 
575 
576 	/**
577 	 * <p>
578 	 * A wrapper for the builders.
579 	 * </p>
580 	 *
581 	 * @param <V>
582 	 *            The type of the value.
583 	 * @param <B>
584 	 *            The type of the builder for the value.
585 	 * @author Gerrit Hohl (gerrit-hohl@users.sourceforge.net)
586 	 * @version <b>1.0</b>, 28.04.2016 by Gerrit Hohl
587 	 */
588 	private abstract class FieldValueBuilder<V, B> {
589 		
590 		
591 		/** The builder. */
592 		private B builder;
593 
594 
595 		/**
596 		 * <p>
597 		 * Creates a wrapper.
598 		 * </p>
599 		 *
600 		 * @param builder
601 		 *            The builder.
602 		 */
603 		public FieldValueBuilder(B builder) {
604 			super();
605 
606 			this.builder = builder;
607 		}
608 
609 
610 		/**
611 		 * <p>
612 		 * Builds the field value from the specified value.
613 		 * </p>
614 		 *
615 		 * @param value
616 		 *            The value.
617 		 * @param context
618 		 *            The context.
619 		 * @return The field value.
620 		 * @throws BuildException
621 		 *             If an error occurs during the building.
622 		 */
623 		public String build(V value, Context context) throws BuildException {
624 			return this.build(this.builder, value, context);
625 		}
626 
627 
628 		/**
629 		 * <p>
630 		 * Builds the field value from the specified value using the specified
631 		 * builder.
632 		 * </p>
633 		 *
634 		 * @param builder
635 		 *            The builder.
636 		 * @param value
637 		 *            The value.
638 		 * @param context
639 		 *            The context.
640 		 * @return The field value.
641 		 * @throws BuildException
642 		 *             If an error occurs during the building.
643 		 */
644 		public abstract String build(B builder, V value, Context context) throws BuildException;
645 
646 
647 	}
648 
649 
650 }