View Javadoc

1   package net.sf.jpkgmk.prototype;
2   
3   import java.io.File;
4   import java.io.FileFilter;
5   import java.io.IOException;
6   import java.util.Arrays;
7   import java.util.Comparator;
8   
9   import net.sf.jpkgmk.DefaultPermissionProvider;
10  import net.sf.jpkgmk.FileHandler;
11  import net.sf.jpkgmk.PermissionProvider;
12  import net.sf.jpkgmk.util.FileUtil;
13  import net.sf.jpkgmk.util.StringUtil;
14  
15  import org.apache.commons.logging.Log;
16  import org.apache.commons.logging.LogFactory;
17  
18  /**
19   * Utility that helps creating a prototype file.
20   * 
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 class PrototypeBuilder 
27  {
28  	private Log log = LogFactory.getLog(PrototypeBuilder.class);
29  
30  	private String defaultUser="root";
31  	private String defaultGroup="other";
32  	private PermissionProvider defaultPermissionProvider=new DefaultPermissionProvider();
33  
34  	private Prototype prototype;
35  	
36  	private File basedir;
37  
38  	private Comparator<File> dirFirstComparator = new Comparator<File>() {
39  	
40  		public int compare(File o1, File o2) {
41  			if(o1.isDirectory() && o2.isDirectory()) {
42  				return o1.compareTo(o2);
43  			}
44  			else if(o1.isFile() && o2.isFile()) {
45  				return o1.compareTo(o2);
46  			}
47  			else if(o1.isDirectory()) {
48  				return -1;
49  			}
50  			else if(o2.isDirectory()) {
51  				return 1;
52  			}
53  			return 0;
54  		}
55  	
56  	};
57  	
58  
59  	/**
60  	 * @see PrototypeBuilder#PrototypeBuilder(File, String, String, PermissionProvider)
61  	 */
62  	public PrototypeBuilder()
63  	{
64  		this(null, null, null, null);
65  	}
66  	
67  	/**
68  	 * @see PrototypeBuilder#PrototypeBuilder(File, String, String, PermissionProvider)
69  	 */
70  	public PrototypeBuilder(File basedir)
71  	{
72  		this(basedir, null, null, null);
73  	}
74  
75  	/**
76  	 * @see PrototypeBuilder#PrototypeBuilder(File, String, String, PermissionProvider)
77  	 */
78  	public PrototypeBuilder(File basedir, String defaultUser, String defaultGroup)
79  	{
80  		this(basedir, defaultUser, defaultGroup, null);
81  	}
82  	
83  	/**
84  	 * @param basedir The basedirectory in which the prototype file is created after invocation of the create() method.
85  	 * @param defaultUser
86  	 * @param defaultGroup
87  	 * @param defaultPermissionProvider
88  	 */
89  	public PrototypeBuilder(File basedir, String defaultUser, String defaultGroup, PermissionProvider defaultPermissionProvider)
90  	{
91  		this.basedir = basedir;
92  		
93  		if(!StringUtil.isNullOrEmpty(defaultUser)) {
94  			this.defaultUser = defaultUser;
95  		}
96  		if(!StringUtil.isNullOrEmpty(defaultGroup)) {
97  			this.defaultGroup = defaultGroup;
98  		}
99  		if(defaultPermissionProvider != null) {
100 			this.defaultPermissionProvider = defaultPermissionProvider;
101 		}
102 		this.prototype = new Prototype();
103 	}
104 	
105 	public String getDefaultUser() {
106 		return defaultUser;
107 	}
108 
109 	public String getDefaultGroup() {
110 		return defaultGroup;
111 	}
112 
113 	public Prototype getPrototype()
114 	{
115 		return this.prototype;
116 	}
117 	
118 	
119 	public void add(PrototypeEntry prototypeEntry) {
120 		this.prototype.add(prototypeEntry);
121 	}
122 
123 	
124 	public void addLink(String linkLocation, PrototypeEntryFile entry) {
125 		PrototypeEntryLink link = new PrototypeEntryLink(linkLocation, entry.getEntryPath());
126 		this.add(link);
127 	}
128 
129 	public void addFile(File sourceFile) throws IOException
130 	{
131 		this.addFile(sourceFile, PrototypeEntryType.F, null);
132 	}
133 	
134 	public PrototypeEntryFile addFile(File sourceFile, PrototypeEntryType type) throws IOException
135 	{
136 		return this.addFile(sourceFile, type, null);
137 	}
138 
139 	public PrototypeEntryFile addFile(File sourceFile, PrototypeEntryType type, String targetPathForPackage) throws IOException
140 	{
141 		if (sourceFile == null) {
142 			throw new NullPointerException(
143 					"The parameter 'sourceFile' must not be null");
144 		}
145 		if (type == null) {
146 			throw new NullPointerException(
147 					"The parameter 'type' must not be null");
148 		}
149 		
150 		if(!sourceFile.isFile()) {
151 			throw new IllegalArgumentException("The file '" + sourceFile + "' must be a file");
152 		}
153 		
154 		PrototypeEntryFile entry = addFileInternal(sourceFile, targetPathForPackage, this.defaultUser, this.defaultGroup, this.defaultPermissionProvider, type);
155 		return entry;
156 	}
157 	
158 	
159 	/**
160 	 * Adds the given directory recursively to this prototype file.
161 	 * All files/subfiles and subdirs are included.
162 	 * @param directory
163 	 */
164 	public void addDirectory(File directory)
165 	{
166 		this.addDirectory(directory, true);
167 	}
168 
169 	/**
170 	 * Adds the given directory recursively to this prototype file.
171 	 * All files/subfiles and subdirs are included.
172 	 * @param directory
173 	 * @param recurse
174 	 */
175 	public void addDirectory(File directory, boolean recurse)
176 	{
177 		this.addDirectory(directory, null, null, null, null, null, recurse);
178 	}
179 
180 	/**
181 	 * Adds the given directory recursively to this prototype file.
182 	 * All files/subfiles and subdirs are included.
183 	 * @param directory
184 	 * @param targetPathDir
185 	 */
186 	public void addDirectory(File directory, String targetPathDir)
187 	{
188 		this.addDirectory(directory, targetPathDir, null, null, null);
189 	}
190 	
191 	/**
192 	 * Adds the given directory recursively to this prototype file.
193 	 * All files/subfiles and subdirs are included.
194 	 * @param directory
195 	 * @param owner
196 	 * @param group
197 	 * @param permissionProvider
198 	 * @param targetPathDir
199 	 */
200 	public void addDirectory(File directory, String targetPathDir, String owner, String group, PermissionProvider permissionProvider)
201 	{
202 		this.addDirectory(directory, targetPathDir, owner, group, permissionProvider, null, true);
203 	}
204 	
205 	/**
206 	 * Adds the given directory recursively to this prototype file.
207 	 * All files/subfiles and subdirs are included.
208 	 * @param directory The directory to be added to the prototype
209 	 * @param targetPathDir Path for this directory on the target machine. Can be null
210 	 * @param owner Owner of the directory and the child files. Can be null
211 	 * @param group Group of the directory and the child files. Can be null
212 	 * @param permissionProvider Permission provider for the given directory and the child files. Can be null
213 	 * @param fileFilter Can be null. Filters out some unwished files.
214 	 * @param recurse If subdirectories should be included
215 	 */
216 	public void addDirectory(File directory, String targetPathDir, String owner, String group, PermissionProvider permissionProvider, FileFilter fileFilter, boolean recurse)
217 	{
218 		log.debug("adding directory '" + directory + "'");
219 		
220 		if(directory == null) {
221 			throw new NullPointerException("The parameter 'directory' must not be null");
222 		}
223 		if(!directory.isDirectory()) {
224 			throw new IllegalArgumentException("The given directory '" + directory + "' is not a directory.");
225 		}
226 		
227 		if(StringUtil.isNullOrEmpty(owner)) {
228 			owner = this.defaultUser;
229 		}
230 		if(StringUtil.isNullOrEmpty(group)) {
231 			group = this.defaultGroup;
232 		}
233 		if(permissionProvider == null) {
234 			permissionProvider = this.defaultPermissionProvider;
235 		}
236 		
237 		addDirectoryInternal(directory, targetPathDir, owner, group, permissionProvider, fileFilter, recurse);
238 	}
239 	
240 	
241 	protected void addDirectoryInternal(File directory, String targetPathDir, String owner, String group, PermissionProvider permissionProvider, FileFilter fileFilter, boolean recurse)
242 	{
243 		log.debug("adding directory '" + directory + "'");
244 	
245 		if(!directory.isDirectory()) {
246 			throw new IllegalArgumentException("The given file must be a directory: '" + directory + "'");
247 		}
248 
249 		// TODO is it possible to use a "Handler" interface so that the user can implement his own class for adding dirs/files? Think about it - similar to PrototypeCommandSearch
250 
251 		File[] children = null;
252 		if(fileFilter == null) {
253 			children = directory.listFiles();
254 		}
255 		else {
256 			children = directory.listFiles(fileFilter);
257 		}
258 		
259 		if(children != null) {
260 			Arrays.sort(children, this.dirFirstComparator);
261 			
262 			// Iterate over the children
263 			for (int i = 0; i < children.length; i++) {
264 				File currentFile = children[i];
265 				// Build the target path for the current dir/file on the unix system
266 				String currentPathForTarget;
267 				if(targetPathDir==null) {
268 					currentPathForTarget = currentFile.getName();
269 				}
270 				else {
271 					// Ensure that the target path dir also exists as entry in the prototype - otherwise the parent dir will be missing
272 					String perm = permissionProvider.getMode(directory);
273 					PrototypeEntry entry = new PrototypeEntryDirectory(null, AbstractPrototypeEntry.DEFAULT_FILE_CLASS, targetPathDir, perm, owner, group, null);
274 					if(!this.prototype.contains(entry)) {
275 						this.prototype.add(entry);
276 					}
277 					currentPathForTarget = targetPathDir + FileUtil.UNIX_FILE_SEPARATOR + currentFile.getName();
278 				}
279 				
280 				if(currentFile.isDirectory()) {
281 					// 1. add directory
282 					createAndAddDirEntry(currentFile, currentPathForTarget, owner, group, permissionProvider, PrototypeEntryType.D);
283 					// 2. recurse this directory
284 					if(recurse) {
285 						log.debug("Recursive call for subdir '" + currentFile + "'");
286 						addDirectoryInternal(currentFile, currentPathForTarget, owner, group, permissionProvider, fileFilter, recurse);
287 					}
288 					else {
289 						log.debug("No recursive call is done for subdir '" + currentFile + "'");
290 					}
291 				}
292 				else {
293 					addFileInternal(currentFile, currentPathForTarget, owner, group, permissionProvider, PrototypeEntryType.F);
294 				}
295 			}
296 		}
297 	}
298 	
299 
300 	private String resolveFilePathOnTarget(String inputFilePathOnTarget, File currentFile) {
301 		if(inputFilePathOnTarget == null) {
302 			inputFilePathOnTarget = currentFile.getName();
303 			log.debug("No 'filePathOnTarget' specified - defaulting to '" + inputFilePathOnTarget + "'.");
304 		}
305 		return inputFilePathOnTarget;
306 	}
307 
308 	private void createAndAddDirEntry(File currentFile, String filePathOnTarget, String owner, String group, PermissionProvider permissionProvider, PrototypeEntryType entryType)
309 	{
310 		String perm = permissionProvider.getMode(currentFile);
311 		filePathOnTarget = resolveFilePathOnTarget(filePathOnTarget,currentFile);
312 		
313 		PrototypeEntry entry;
314 		if(entryType == PrototypeEntryType.D) {
315 			entry = new PrototypeEntryDirectory(null, AbstractPrototypeEntry.DEFAULT_FILE_CLASS, filePathOnTarget, perm, owner, group, null);
316 		}
317 		else if(entryType == PrototypeEntryType.X) {
318 			entry = new PrototypeEntryDirectoryExclusive(null, AbstractPrototypeEntry.DEFAULT_FILE_CLASS, filePathOnTarget, perm, owner, group, null);
319 		}
320 		else {
321 			throw new IllegalArgumentException("The entryType '" + entryType + "' is not yet supported");
322 		}
323 
324         this.prototype.add(entry);
325 	}
326 
327 	
328 	protected PrototypeEntryFile addFileInternal(File currentFile, String filePathOnTarget, String owner, String group, PermissionProvider permissionProvider, PrototypeEntryType entryType) 
329 	{
330 		String perm = permissionProvider.getMode(currentFile);
331 		String sourceFile = currentFile.getAbsolutePath();
332 		filePathOnTarget = resolveFilePathOnTarget(filePathOnTarget,currentFile);
333 
334 		PrototypeEntryFile entry;
335 		if(entryType == PrototypeEntryType.F) {
336 			entry = new PrototypeEntryFile(null, AbstractPrototypeEntry.DEFAULT_FILE_CLASS, filePathOnTarget, sourceFile, perm, owner, group, null);
337 		}
338 		else if(entryType == PrototypeEntryType.E) {
339 			entry = new PrototypeEntryFileEditable(null, AbstractPrototypeEntry.DEFAULT_FILE_CLASS, filePathOnTarget, sourceFile, perm, owner, group, null);
340 		}
341 		else if(entryType == PrototypeEntryType.V) {
342 			entry = new PrototypeEntryFileVolatile(null, AbstractPrototypeEntry.DEFAULT_FILE_CLASS, filePathOnTarget, sourceFile, perm, owner, group, null);
343 		}
344 		else if(entryType == PrototypeEntryType.I) {
345 			entry = new PrototypeEntryInfo(null, filePathOnTarget, sourceFile);
346 		}
347 //		else if(entryType == PrototypeEntryType.S) {
348 //			entry = new PrototypeEntrySymbolicLink(null, filePathOnTarget, sourceFile);
349 //		}
350 //		else if(entryType == PrototypeEntryType.L) {
351 //			throw new IllegalArgumentException("Cannot add a physical file as link. You must add the file first and then the link using strings.");
352 //			entry = new PrototypeEntryLink(null, filePathOnTarget, sourceFile);
353 //		}
354 		else {
355 			throw new IllegalArgumentException("The entryType '" + entryType + "' is not yet supported");
356 		}
357 		
358 		// Ensure that a file class is set
359 		// TODO is it possible to use a "Handler" interface so that the user can implement his own class for adding dirs/files? Think about it - similar to PrototypeCommandSearch
360 
361         this.prototype.add(entry);
362         return entry;
363 	}
364 
365 
366 	private void checkBasedirIsSet(){
367 		if (basedir == null) {
368 			throw new NullPointerException("The attribute 'basedir' must not be null");
369 		}
370 	}
371 
372 	public File clean() {
373 		checkBasedirIsSet();
374 		getPrototype().clean(basedir);
375 		return getFile();
376 	}
377 
378 	public File create() throws IOException {
379 		checkBasedirIsSet();
380 		getPrototype().create(basedir);
381 		return getFile();
382 	}
383 	
384 	public File getFile(){
385 		checkBasedirIsSet();
386 		FileHandler fileHandler = getPrototype().getFileHandler(basedir);
387 		return fileHandler.getFile();
388 	}
389 	
390     @Override
391 	public String toString()
392 	{
393 		StringBuffer sb = new StringBuffer();
394 		sb.append(getClass().getName()).append("[");
395 		sb.append("basedir=").append(basedir);
396 		sb.append(",defaultUser=").append(defaultUser);
397 		sb.append(",defaultGroup=").append(defaultGroup);
398 		sb.append(",defaultPermissionProvider=").append(defaultPermissionProvider);
399 		sb.append(",prototype=").append(this.prototype);
400 		sb.append("]");
401 		return sb.toString();
402 	}
403 
404 }