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.store;
20  
21  import java.io.IOException;
22  import java.security.MessageDigest;
23  import java.util.List;
24  
25  import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
26  
27  import net.sourceforge.javadpkg.DebianPackageConstants;
28  import net.sourceforge.javadpkg.control.Size;
29  import net.sourceforge.javadpkg.io.DataSource;
30  import net.sourceforge.javadpkg.io.FileMode;
31  import net.sourceforge.javadpkg.io.FileOwner;
32  import net.sourceforge.javadpkg.io.impl.FileModeImpl;
33  import net.sourceforge.javadpkg.io.impl.FileOwnerImpl;
34  
35  
36  /**
37   * <p>
38   * A {@link DataStore} implementation.
39   * </p>
40   *
41   * @author Gerrit Hohl (gerrit-hohl@users.sourceforge.net)
42   * @version <b>1.0</b>, 26.04.2016 by Gerrit Hohl
43   */
44  public class DataStoreImpl implements DataStore, DebianPackageConstants {
45  	
46  	
47  	/** The default owner. */
48  	private static final FileOwner	DEFAULT_OWNER			= new FileOwnerImpl(ROOT_GROUP_ID, ROOT_GROUP_NAME, ROOT_USER_ID,
49  			ROOT_USER_NAME);
50  
51  	/** The default mode for a directory. */
52  	private static final FileMode	DEFAULT_DIRECTORY_MODE	= new FileModeImpl(DIRECTORY_MODE);
53  	/** The default mode for a regular file. */
54  	private static final FileMode	DEFAULT_FILE_MODE		= new FileModeImpl(FILE_MODE);
55  
56  
57  	/** The root node. */
58  	private DataStoreNode			rootNode;
59  
60  
61  	/**
62  	 * <p>
63  	 * Creates a store.
64  	 * </p>
65  	 */
66  	public DataStoreImpl() {
67  		super();
68  
69  		this.rootNode = new DataStoreNode();
70  	}
71  	
72  	
73  	/**
74  	 * <p>
75  	 * Splits the specified path into its parts.
76  	 * </p>
77  	 *
78  	 * @param path
79  	 *            The path.
80  	 * @return The path parts.
81  	 */
82  	private String[] getPathParts(String path) {
83  		String line;
84  		String[] pathParts;
85  
86  
87  		line = path;
88  		if (line.startsWith("./")) {
89  			line = line.substring(2);
90  		} else if (line.startsWith(".")) {
91  			line = line.substring(1);
92  		} else if (line.startsWith("/")) {
93  			line = line.substring(1);
94  		}
95  		pathParts = line.split("/", 0);
96  		return pathParts;
97  	}
98  	
99  	
100 	/**
101 	 * <p>
102 	 * Returns the node with the specified path.
103 	 * </p>
104 	 *
105 	 * @param pathParts
106 	 *            The path parts.
107 	 * @param index
108 	 *            The index of parent node in the path parts. Starts at
109 	 *            <code>0</code> with the root node.
110 	 * @param level
111 	 *            The level of the node which should be returned within the path
112 	 *            parts. If level is <code>pathParts.length - 1</code> the node
113 	 *            referred by the complete path will be returned.
114 	 * @param parentNode
115 	 *            The parent node.
116 	 * @return The node or <code>null</code>, if no such node exists.
117 	 */
118 	private DataStoreNode getNode(String[] pathParts, int index, int level, DataStoreNode parentNode) {
119 		String name;
120 		DataStoreNode node;
121 		
122 		if (index >= level)
123 			return parentNode;
124 		
125 		name = pathParts[index];
126 		node = parentNode.getChildNodeByName(name);
127 		if (node == null)
128 			return null;
129 		return this.getNode(pathParts, index + 1, level, node);
130 	}
131 
132 
133 	/**
134 	 * <p>
135 	 * Adds a node.
136 	 * </p>
137 	 *
138 	 * @param source
139 	 *            The source (optional). Only needed if a file node is added.
140 	 * @param path
141 	 *            The path.
142 	 * @param target
143 	 *            The target (optional). Only needed if a symbolic link node is
144 	 *            added.
145 	 * @param owner
146 	 *            The owner.
147 	 * @param mode
148 	 *            The mode.
149 	 * @throws IllegalArgumentException
150 	 *             If the path is invalid, the parent node is not a directory,
151 	 *             the parent directory doesn't exist or already contains an
152 	 *             entry with the same name.
153 	 */
154 	private void addNode(DataSource source, String path, String target, FileOwner owner, FileMode mode) {
155 		String type, name;
156 		String[] pathParts;
157 		DataStoreNode parentNode, node;
158 		
159 		
160 		// --- Does the path point to the root node? ---
161 		pathParts = this.getPathParts(path);
162 		if ((pathParts.length == 0) || ((pathParts.length == 1) && pathParts[0].isEmpty()))
163 			throw new IllegalArgumentException("Argument path doesn't contain a path: " + path);
164 		
165 		// --- Prepare type for error messages ---
166 		if (source != null) {
167 			type = "file";
168 		} else if (target != null) {
169 			type = "symbolic link";
170 		} else {
171 			type = "directory";
172 		}
173 		
174 		// --- Get parent node ---
175 		parentNode = this.getNode(pathParts, 0, pathParts.length - 1, this.rootNode);
176 		if (parentNode == null)
177 			throw new IllegalArgumentException(
178 					"Can't add " + type + " |" + path + "| because the parent directory doesn't exist.");
179 		if (!parentNode.isDirectory())
180 			throw new IllegalArgumentException("Can't add " + type + " |" + path + "| because the entry |"
181 					+ parentNode.getPath() + "| is not a directory.");
182 		
183 		// --- Does the node already exist? ---
184 		name = pathParts[pathParts.length - 1];
185 		node = parentNode.getChildNodeByName(name);
186 		if (node != null)
187 			throw new IllegalArgumentException("Can't add " + type + " because a " + (node.isDirectory() ? "directory" : "file")
188 					+ " with the path |" + path + "| already exists.");
189 		
190 		// --- Create node for file, symbolic link or directory ---
191 		if (source != null) {
192 			node = new DataStoreNode(source, name, owner, mode);
193 		} else if (target != null) {
194 			node = new DataStoreNode(name, target, owner, mode);
195 		} else {
196 			node = new DataStoreNode(name, owner, mode);
197 		}
198 		parentNode.addChildNode(node);
199 	}
200 	
201 	
202 	@Override
203 	public void addDirectory(String path) {
204 		if (path == null)
205 			throw new IllegalArgumentException("Argument path is null.");
206 		
207 		this.addDirectory(path, DEFAULT_OWNER, DEFAULT_DIRECTORY_MODE);
208 	}
209 
210 
211 	@Override
212 	public void addDirectory(String path, FileOwner owner, FileMode mode) {
213 		if (path == null)
214 			throw new IllegalArgumentException("Argument path is null.");
215 		if (owner == null)
216 			throw new IllegalArgumentException("Argument owner is null.");
217 		if (mode == null)
218 			throw new IllegalArgumentException("Argument mode is null.");
219 		
220 		this.addNode(null, path, null, owner, mode);
221 	}
222 
223 
224 	@Override
225 	public void addFile(DataSource source, String path) {
226 		if (source == null)
227 			throw new IllegalArgumentException("Argument source is null.");
228 		if (path == null)
229 			throw new IllegalArgumentException("Argument path is null.");
230 		
231 		this.addFile(source, path, DEFAULT_OWNER, DEFAULT_FILE_MODE);
232 	}
233 
234 
235 	@Override
236 	public void addFile(DataSource source, String path, FileOwner owner, FileMode mode) {
237 		
238 		if (source == null)
239 			throw new IllegalArgumentException("Argument source is null.");
240 		if (path == null)
241 			throw new IllegalArgumentException("Argument path is null.");
242 		if (owner == null)
243 			throw new IllegalArgumentException("Argument owner is null.");
244 		if (mode == null)
245 			throw new IllegalArgumentException("Argument mode is null.");
246 		
247 		this.addNode(source, path, null, owner, mode);
248 	}
249 	
250 	
251 	@Override
252 	public void addSymLink(String path, String target, FileOwner owner, FileMode mode) {
253 		if (path == null)
254 			throw new IllegalArgumentException("Argument path is null.");
255 		if (target == null)
256 			throw new IllegalArgumentException("Argument target is null.");
257 		if (owner == null)
258 			throw new IllegalArgumentException("Argument owner is null.");
259 		if (mode == null)
260 			throw new IllegalArgumentException("Argument mode is null.");
261 		
262 		this.addNode(null, path, target, owner, mode);
263 	}
264 
265 
266 	@Override
267 	public boolean exists(String path) {
268 		String[] pathParts;
269 		DataStoreNode node;
270 
271 		if (path == null)
272 			throw new IllegalArgumentException("Argument path is null.");
273 		
274 		// --- Does the path point to the root node? ---
275 		pathParts = this.getPathParts(path);
276 		if ((pathParts.length == 0) || ((pathParts.length == 1) && pathParts[0].isEmpty()))
277 			throw new IllegalArgumentException("Argument path doesn't contain a path: " + path);
278 		
279 		// --- Get node ---
280 		node = this.getNode(pathParts, 0, pathParts.length, this.rootNode);
281 		return (node != null);
282 	}
283 
284 
285 	@Override
286 	public Size getSize() throws IOException {
287 		Size size;
288 		
289 		
290 		try {
291 			size = new Size(this.rootNode.getSize());
292 		} catch (IOException e) {
293 			throw new IOException("Couldn't determine size of store: " + e.getMessage(), e);
294 		}
295 		return size;
296 	}
297 
298 
299 	@Override
300 	public void write(TarArchiveOutputStream out) throws IOException {
301 		
302 		if (out == null)
303 			throw new IllegalArgumentException("Argument out is null.");
304 		
305 		this.rootNode.write(out);
306 	}
307 	
308 	
309 	@Override
310 	public List<FileHash> createFileHashes(MessageDigest digest) throws IOException {
311 		List<FileHash> hashes;
312 
313 
314 		if (digest == null)
315 			throw new IllegalArgumentException("Argument digest is null.");
316 		
317 		hashes = this.rootNode.createFileHashes(digest);
318 		return hashes;
319 	}
320 
321 
322 }