View Javadoc

1   package net.sf.jpkgmk.prototype;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.util.ArrayList;
6   import java.util.List;
7   
8   import net.sf.jpkgmk.AbstractLineProvider;
9   import net.sf.jpkgmk.pkgmap.PkgMapEntry;
10  import net.sf.jpkgmk.pkgmap.PkgMapEntryType;
11  import net.sf.jpkgmk.util.FileUtil;
12  import net.sf.jpkgmk.util.StringUtil;
13  import net.sf.jpkgmk.util.VariableMap;
14  import net.sf.jpkgmk.util.VariableResolver;
15  
16  import org.apache.commons.logging.Log;
17  import org.apache.commons.logging.LogFactory;
18  
19  /**
20   * Abstract class that can be used for the most of the prototype entries, for example files, directories, links ...
21   * @author gommma (gommma AT users.sourceforge.net)
22   * @author Last changed by: $Author: gommma $
23   * @version $Revision: 2 $ $Date: 2008-08-20 21:14:19 +0200 (Mi, 20 Aug 2008) $
24   * @since 1.0
25   */
26  public abstract class AbstractPrototypeEntry extends AbstractLineProvider implements PrototypeEntry
27  {
28  	private Log log = LogFactory.getLog(AbstractPrototypeEntry.class);
29  	
30  	public static final String DEFAULT_FILE_CLASS = "none";
31  
32  	/**
33  	 * An optional field designating the part number in which the object resides. 
34  	 * A part is a collection of files and is the atomic unit by which a package is processed. 
35  	 * A developer can choose criteria for grouping files into a part (for example, based on class). 
36  	 * If this field is not used, part 1 is assumed. (field is omitted in prototype file)
37  	 */
38  	private Integer part;
39  	private PrototypeEntryType type;
40  	private String entryPath;
41  	private String entryPathSource;
42  	private String fileClass;
43  	private Integer major;
44  	private Integer minor;
45  	private String mode;
46  	private String owner;
47  	private String group;
48  	/**
49  	 * The "DEFAULT" command that can provide default values for mode/owner/group if not set in this entry
50  	 */
51  	private PrototypeEntryCommandDefault entryCommandDefault;
52  	
53  	
54  	/**
55  	 * Constructor taking the mandatory arguments to create a prototype file entry
56  	 * @param fileType
57  	 * @param value
58  	 */
59  	public AbstractPrototypeEntry(PrototypeEntryType fileType, String entryPath)
60  	{
61  		this(fileType, null, entryPath, null, null, null, null, null);
62  	}
63  
64  	/**
65  	 * @param fileType
66  	 * @param fileClass
67  	 * @param entryPath the target path in that this file/directory should have in the package
68  	 * @param mode
69  	 * @param owner
70  	 * @param group
71  	 */
72  	public AbstractPrototypeEntry(PrototypeEntryType fileType, String entryPath, String mode, String owner, String group)
73  	{
74  		this(fileType, null, entryPath, null, mode, owner, group, null);
75  	}
76  	
77  	/**
78  	 * @param fileType
79  	 * @param fileClass
80  	 * @param entryPath the target path in that this file/directory should have in the package
81  	 * @param entryPathSource path to the local source file
82  	 * @param mode
83  	 * @param owner
84  	 * @param group
85  	 */
86  	public AbstractPrototypeEntry(PrototypeEntryType fileType, String fileClass, String entryPath, String entryPathSource, String mode, String owner, String group, PrototypeEntryCommandDefault entryCommandDefault)
87  	{
88  		this(null, fileType, fileClass, entryPath, entryPathSource, null, null, mode, owner, group, entryCommandDefault);
89  	}
90  
91  	
92  	/**
93  	 * @param part
94  	 * @param fileType
95  	 * @param fileClass
96  	 * @param entryPath the target path that this file/directory should have in the created package
97  	 * @param entryPathSource path to the local source file
98  	 * @param mode
99  	 * @param owner
100 	 * @param group
101 	 */
102 	public AbstractPrototypeEntry(Integer part, PrototypeEntryType fileType, String fileClass, String entryPath, String entryPathSource, String mode, String owner, String group, PrototypeEntryCommandDefault entryCommandDefault)
103 	{
104 		this(part, fileType, fileClass, entryPath, entryPathSource, null, null, mode, owner, group, entryCommandDefault);
105 	}
106 
107 	/**
108 	 * Full constructor taking all possible arguments
109 	 * @param part Optional part number
110 	 * @param fileType
111 	 * @param fileClass
112 	 * @param entryPath the target path in that this file/directory should have in the package
113 	 * @param entryPathSource path to the local source file
114 	 * @param major The major device number. The field is only specified for block or character special devices.
115 	 * @param minor The minor device number. The field is only specified for block or character special devices.
116 	 * @param mode
117 	 * @param owner
118 	 * @param group
119 	 * @param entryCommandDefault
120 	 */
121 	public AbstractPrototypeEntry(Integer part, PrototypeEntryType fileType, String fileClass, String entryPath, String entryPathSource, Integer major, Integer minor, String mode, String owner, String group, PrototypeEntryCommandDefault entryCommandDefault)
122 	{
123 		// Mandatory parameter check
124 		if(fileType == null) {
125 			throw new NullPointerException("The parameter 'fileType' must not be null");
126 		}
127 		if(entryPath == null) {
128 			throw new NullPointerException("The parameter 'entryPath' must not be null");
129 		}
130 		
131 		if(fileClass != null) {
132 			validateFileClass(fileClass);
133 		}
134 		
135 		if(mode!=null && !VariableMap.containsVariable(mode)) {
136 			StringUtil.validateMode(mode);
137 		}
138 		
139 		if(owner!=null && !VariableMap.containsVariable(owner)) {
140 			validateOwner(owner);
141 		}
142 		if(group!=null && !VariableMap.containsVariable(group)) {
143 			validateGroup(group);
144 		}
145 
146 		
147 		
148 		this.part = part;
149 		this.type = fileType;
150 		this.fileClass = fileClass;
151 		this.entryPath = entryPath;
152 		this.entryPathSource = entryPathSource;
153 		this.mode = mode;
154 		this.owner = owner;
155 		this.group = group;
156 		this.major = major;
157 		this.minor = minor;
158 		this.entryCommandDefault = entryCommandDefault;
159 	}
160 
161 	private void validateOwner(String owner) {
162 		int maxOwnerLength = 14;
163 		if(owner.length() > maxOwnerLength) {
164 			throw new IllegalArgumentException("The given owner '" + owner + "' exceeds the maximum allowed length of " + maxOwnerLength);
165 		}
166 	}
167 
168 	private void validateGroup(String group) {
169 		int maxGroupLength = 14;
170 		if(group.length() > maxGroupLength) {
171 			throw new IllegalArgumentException("The given group '" + group + "' exceeds the maximum allowed length of " + maxGroupLength);
172 		}
173 	}
174 
175 	private void validateFileClass(String fileClassToValidate) {
176 		int maxClassLength = 12;
177 		if(fileClassToValidate.length() > maxClassLength) {
178 			throw new IllegalArgumentException("The given class '" + fileClassToValidate + "' exceeds the maximum allowed length of " + maxClassLength);
179 		}
180 		// Check if class is alphanumeric
181 		if(!StringUtil.isAlphaNumeric(fileClassToValidate)) {
182 			throw new IllegalArgumentException("The given class '" + fileClassToValidate + "' is not alphanumeric.");
183 		}
184 
185 		// ("admin" and all classes beginning with capital letters are reserved class names.)
186 		if(fileClassToValidate.equals("admin")) {
187 			throw new IllegalArgumentException("The given class '" + fileClassToValidate + "' is a reserved word and must not be used ('admin' is a reserved word).");
188 		}
189 		if(Character.isUpperCase(fileClassToValidate.charAt(0))) {
190 			throw new IllegalArgumentException("The given class '" + fileClassToValidate + "' is a reserved word and must not be used (it begins with a capital letter).");
191 		}
192 	}
193 
194 	/**
195 	 * @param fileType
196 	 * @param fileClass
197 	 * @param value Key=Value pair or directory path
198 	 * @param perm
199 	 * @param owner
200 	 * @param group
201 	 * @return
202 	 * @throws IOException
203 	 */
204 	private String createLine()
205 	{
206 		StringBuffer completeLine = new StringBuffer();
207 		
208 		if(this.part != null) {
209 			completeLine.append(this.part);
210 		}
211 		
212 		if(completeLine.length()>0) {
213 			completeLine.append(" "); // add separator
214 		}
215 		completeLine.append(type.getKey());
216 
217 		// Class
218 		if(!StringUtil.isNullOrEmpty(fileClass)) {
219 			completeLine.append(" ").append(fileClass);
220 		}
221 		// Pathname
222 		completeLine.append(" ").append(entryPath);
223 		if(!StringUtil.isNullOrEmpty(entryPathSource)) {
224 			completeLine.append("=").append(entryPathSource);
225 		}
226 		if(major != null) {
227 			completeLine.append(" ").append(major);
228 		}
229 		if(minor != null) {
230 			completeLine.append(" ").append(minor);
231 		}
232 		if(!StringUtil.isNullOrEmpty(mode)) {
233 			completeLine.append(" ").append(mode);
234 		}
235 		if(!StringUtil.isNullOrEmpty(owner)) {
236 			completeLine.append(" ").append(owner);
237 		}
238 		if(!StringUtil.isNullOrEmpty(group)) {
239 			completeLine.append(" ").append(group);
240 		}
241 		return completeLine.toString();
242 	}
243 	
244 	
245 	public String getLine()
246 	{
247 		return this.createLine();
248 	}
249 
250 	public PrototypeEntryType getType() {
251 		return this.type;
252 	}
253 	
254 	public String getEntryPath() {
255 		return entryPath;
256 	}
257 
258 	public String getEntryPathSource() {
259 		return entryPathSource;
260 	}
261 
262 	public String getFileClass() {
263 		return fileClass;
264 	}
265 
266 	public String getMode() {
267 		return mode;
268 	}
269 
270 	public String getOwner() {
271 		return owner;
272 	}
273 
274 	public String getGroup() {
275 		return group;
276 	}
277 
278 	
279 	public Integer getMajor() {
280 		return major;
281 	}
282 
283 	public Integer getMinor() {
284 		return minor;
285 	}
286 
287 	protected final String resolveMode() {
288 		if(!StringUtil.isNullOrEmpty(this.mode)) {
289 			return this.mode;
290 		}
291 		else if(entryCommandDefault != null){
292 			return entryCommandDefault.getMode();
293 		}
294 		else {
295 			return null;
296 		}
297 	}
298 
299 	protected final String resolveOwner() 
300 	{
301 		if(!StringUtil.isNullOrEmpty(this.owner)) {
302 			return this.owner;
303 		}
304 		else if(entryCommandDefault != null){
305 			return entryCommandDefault.getOwner();
306 		}
307 		else {
308 			return null;
309 		}
310 	}
311 
312 	protected final String resolveGroup() 
313 	{
314 		if(!StringUtil.isNullOrEmpty(this.group)) {
315 			return this.group;
316 		}
317 		else if(entryCommandDefault != null){
318 			return entryCommandDefault.getGroup();
319 		}
320 		else {
321 			return null;
322 		}
323 	}
324 
325 //	protected File resolveTarget(File targetDir) {
326 //		File target;
327 //		// Is relocatable? (relocatable=relative path)
328 //		if(FileUtil.isRelocatable(this.entryPath)) {
329 ////		if(!new File(entryPath).isAbsolute()) {
330 //			target = new File(targetDir, this.entryPath);
331 //		}
332 //		else {
333 //			target = new File(this.entryPath);
334 //		}
335 //		return target;
336 //	}
337 
338 	protected File buildAbsolutePath(File targetDir, String prototypeEntryPath)
339 	{
340 		File target;
341 		// Is relocatable? (relocatable=relative path)
342 		if(FileUtil.isRelocatable(prototypeEntryPath)) {
343 			target = new File(targetDir, prototypeEntryPath);
344 		}
345 		else {
346 			target = new File(prototypeEntryPath);
347 		}
348 		return target;
349 	}
350 	
351 	/**
352 	 * Copies the file of this prototype entry into the target directory
353 	 * @see net.sf.jpkgmk.prototype.PrototypeEntry#create(java.io.File, VariableResolver)
354 	 */
355 	public final void create(File targetDir, VariableResolver variableResolver) throws IOException
356 	{
357 		String expandedEntryPath = variableResolver.expand(entryPath);
358 		String expandedEntryPathSource = variableResolver.expand(entryPathSource);
359 		this.create(targetDir, expandedEntryPath, expandedEntryPathSource);
360 	}
361 	
362 	/**
363 	 * Copies the file of this prototype entry into the target directory
364 	 */
365 	protected abstract void create(File targetDir, String entryPathExpanded, String entryPathSourceExpanded) throws IOException;
366 
367 
368 	/**
369 	 * @see net.sf.jpkgmk.prototype.PrototypeEntry#createPkgMapEntry(java.io.File, net.sf.jpkgmk.util.VariableResolver)
370 	 */
371 	public List<PkgMapEntry> createPkgMapEntry(File targetDir, VariableResolver variableResolver) {
372 
373 		if (variableResolver == null) {
374 			throw new NullPointerException("The parameter 'variableResolver' must not be null");
375 		}
376 		
377 		PkgMapEntryType resultType = this.type.getPkgMapEntryType();
378 		
379 		String expandedEntryPath = variableResolver.expand(entryPath);
380 		String expandedEntryPathSource = variableResolver.expand(entryPathSource);
381 		
382 		String entryClass = null;
383 		if(StringUtil.isNullOrEmpty(this.fileClass)) {
384 			entryClass = DEFAULT_FILE_CLASS;
385 			log.debug("entryClass is null. Defaulting the file class to " + fileClass + "' for entry " + this);
386 		}
387 		else {
388 			entryClass =this.fileClass;
389 		}
390 		
391 		// mode, owner, group can have variables as well
392 		String mode = resolveMode();
393 		mode = variableResolver.expand(mode);
394 		String owner = resolveOwner();
395 		owner = variableResolver.expand(owner);
396 		String group = resolveGroup();
397 		group = variableResolver.expand(group);
398 		
399 		if(mode!=null)
400 			StringUtil.validateMode(mode);
401 		if(owner!=null)
402 			validateOwner(owner);
403 		if(group!=null)
404 			validateGroup(group);
405 
406 		
407 		// Invoke the abstract implementation
408 		PkgMapEntry resultEntry = createPkgMapEntry(this.part, resultType, entryClass, expandedEntryPath, expandedEntryPathSource, major, minor, mode, owner, group, targetDir);
409 
410 		List<PkgMapEntry> result = new ArrayList<PkgMapEntry>();
411 		result.add(resultEntry);
412 		return result;
413 	}
414 
415 	protected abstract PkgMapEntry createPkgMapEntry(Integer part,
416 			PkgMapEntryType resultType, String entryClass, String entryPath, String entryPathSource,
417 			Integer major, Integer minor, String mode, String owner, String group, File targetDir);
418 	
419 	
420 	
421 	//TODO needed for PrototypeEntryLink and PrototypeEntryLinkSymbolic?
422 //	private String buildPathForPkgMap() {
423 //		// only links may have the "=" assignment
424 //		String path = this.entryPath;
425 //		if(this.entryPathSource != null) {
426 //			if(this.type == PrototypeEntryType.L || this.type == PrototypeEntryType.S) {
427 //				path += "=" + this.entryPathSource;
428 //			}
429 //			else {
430 //				log.debug("The member entryPathSource='" + entryPathSource + "' is not used. No linked file.");
431 //			}
432 //		}
433 //		else {
434 //			log.debug("The member 'entryPathSource' is null. Not added to path");
435 //		}
436 //		return path;
437 //	}
438 
439 
440 
441 	@Override
442 	public int hashCode() {
443 		final int prime = 31;
444 		int result = 1;
445 		result = prime * result
446 				+ ((entryPath == null) ? 0 : entryPath.hashCode());
447 		return result;
448 	}
449 
450 	@Override
451 	public boolean equals(Object obj) {
452 		if (this == obj)
453 			return true;
454 		if (obj == null)
455 			return false;
456 		if (getClass() != obj.getClass())
457 			return false;
458 		final AbstractPrototypeEntry other = (AbstractPrototypeEntry) obj;
459 		if (entryPath == null) {
460 			if (other.entryPath != null)
461 				return false;
462 		} else if (!entryPath.equals(other.entryPath))
463 			return false;
464 		return true;
465 	}
466 
467 	@Override
468 	public String toString()
469 	{
470 		return this.getLine();
471 	}
472 
473 }