3 * Copyright © 2014 František Kučera (frantovo.cz)
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package cz.frantovo.alt2xml.in.fs;
20 import cz.frantovo.alt2xml.AbstractAlt2XmlReader;
22 import java.io.IOException;
24 import java.net.URISyntaxException;
25 import java.nio.ByteBuffer;
26 import java.nio.file.Files;
27 import java.nio.file.LinkOption;
28 import java.nio.file.Path;
29 import java.nio.file.attribute.PosixFilePermission;
30 import java.nio.file.attribute.UserDefinedFileAttributeView;
31 import java.nio.file.spi.FileSystemProvider;
32 import java.util.List;
33 import java.util.ArrayList;
35 import java.util.logging.Logger;
36 import org.xml.sax.Attributes;
37 import org.xml.sax.InputSource;
38 import org.xml.sax.SAXException;
39 import org.xml.sax.helpers.AttributesImpl;
42 * Reads filesystem – hierarchical structure of directories and files.
44 * @author Ing. František Kučera (frantovo.cz)
46 public class Reader extends AbstractAlt2XmlReader {
48 public static final String ROOT_ELEMENT = "fs";
49 public static final String DIR_ELEMENT = "dir";
50 public static final String FILE_ELEMENT = "file";
51 public static final String XATTR_ELEMENT = "xattr";
52 public static final String PERMISSIONS_ELEMENT = "mode";
53 public static final String PERMISSIONS_U_ELEMENT = "user";
54 public static final String PERMISSIONS_G_ELEMENT = "group";
55 public static final String PERMISSIONS_O_ELEMENT = "others";
56 public static final String NAME_ATTRIBUTE = "name";
57 public static final String ABSOLUTE_PATH_ATTRIBUTE = "path";
58 public static final String SIZE_ATTRIBUTE = "size";
59 private static final Logger log = Logger.getLogger(Reader.class.getName());
64 private int level = 0;
67 public void parse(InputSource input) throws IOException, SAXException {
68 File dir = getFile(input.getSystemId());
75 private File getFile(String systemId) throws IOException {
77 return new File(new URI(systemId));
78 } catch (URISyntaxException e) {
79 throw new IOException("Invalid dir URI", e);
83 private String getAbsolutePath(File file) throws IOException {
84 return file.getCanonicalFile().getAbsolutePath();
87 private Attributes singleAttribute(String name, int value) {
88 AttributesImpl attributes = new AttributesImpl();
89 addAttribute(attributes, name, value);
93 private Attributes singleAttribute(String name, String value) {
94 AttributesImpl attributes = new AttributesImpl();
95 addAttribute(attributes, name, value);
99 private void addAttribute(AttributesImpl attributes, String name, int value) {
100 attributes.addAttribute(null, name, name, "xs:int", String.valueOf(value));
103 private void addAttribute(AttributesImpl attributes, String name, long value) {
104 attributes.addAttribute(null, name, name, "xs:long", String.valueOf(value));
107 private void addAttribute(AttributesImpl attributes, String name, String value) {
108 attributes.addAttribute(null, name, name, "xs:string", value);
111 private void outputStart(File root) throws SAXException, IOException {
112 contentHandler.startDocument();
113 contentHandler.lineBreak();
114 contentHandler.startElement(null, null, ROOT_ELEMENT, singleAttribute(ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(root)));
115 contentHandler.lineBreak();
118 private void outputEnd() throws SAXException {
119 contentHandler.endElement(null, null, ROOT_ELEMENT);
120 contentHandler.lineBreak();
121 contentHandler.endDocument();
124 private void outputFile(File file) throws SAXException, IOException {
127 AttributesImpl attributes = new AttributesImpl();
128 addAttribute(attributes, NAME_ATTRIBUTE, file.getName());
129 addAttribute(attributes, ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(file));
130 addAttribute(attributes, SIZE_ATTRIBUTE, file.length());
132 contentHandler.indentation(level);
133 contentHandler.startElement(null, null, FILE_ELEMENT, attributes);
134 contentHandler.lineBreak();
136 outputPermissions(file.toPath());
137 outputExtendedAttributes(file);
139 contentHandler.indentation(level);
140 contentHandler.endElement(null, null, FILE_ELEMENT);
141 contentHandler.lineBreak();
146 private void outputDir(File dir) throws SAXException, IOException {
149 final File[] children = dir.listFiles();
151 AttributesImpl attributes = new AttributesImpl();
152 addAttribute(attributes, NAME_ATTRIBUTE, dir.getName());
153 addAttribute(attributes, ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(dir));
154 addAttribute(attributes, SIZE_ATTRIBUTE, children.length);
156 contentHandler.indentation(level);
157 contentHandler.startElement(null, null, DIR_ELEMENT, attributes);
158 contentHandler.lineBreak();
160 outputPermissions(dir.toPath());
161 outputExtendedAttributes(dir);
163 for (File file : children) {
164 if (file.isDirectory()) {
171 contentHandler.indentation(level);
172 contentHandler.endElement(null, null, DIR_ELEMENT);
173 contentHandler.lineBreak();
178 private String encodeElementName(String originalName) {
180 * TODO: encode and/or skip invalid characters
185 private void outputPermissions(Path path) throws IOException, SAXException {
187 Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path); // TODO: maybe symlinks: LinkOption.NOFOLLOW_LINKS
189 contentHandler.indentation(level);
190 contentHandler.startElement(null, PERMISSIONS_ELEMENT, PERMISSIONS_ELEMENT, singleAttribute("octal", getOctal(permissions)));
191 contentHandler.lineBreak();
195 outputPermission("owner", permissions.contains(PosixFilePermission.OWNER_READ), permissions.contains(PosixFilePermission.OWNER_WRITE), permissions.contains(PosixFilePermission.OWNER_EXECUTE));
196 outputPermission("group", permissions.contains(PosixFilePermission.GROUP_READ), permissions.contains(PosixFilePermission.GROUP_WRITE), permissions.contains(PosixFilePermission.GROUP_EXECUTE));
197 outputPermission("others", permissions.contains(PosixFilePermission.OTHERS_READ), permissions.contains(PosixFilePermission.OTHERS_WRITE), permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
201 contentHandler.indentation(level);
202 contentHandler.endElement(null, PERMISSIONS_ELEMENT, PERMISSIONS_ELEMENT);
203 contentHandler.lineBreak();
208 private int getOctal(Set<PosixFilePermission> permissions) {
211 octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_READ) ? 4 : 0);
212 octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_WRITE) ? 2 : 0);
213 octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_EXECUTE) ? 1 : 0);
215 octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_READ) ? 4 : 0);
216 octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_WRITE) ? 2 : 0);
217 octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_EXECUTE) ? 1 : 0);
219 octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_READ) ? 4 : 0);
220 octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_WRITE) ? 2 : 0);
221 octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_EXECUTE) ? 1 : 0);
226 private void outputPermission(String name, boolean read, boolean write, boolean execute) throws SAXException {
227 contentHandler.indentation(level);
228 AttributesImpl attributes = new AttributesImpl();
229 attributes.addAttribute(null, "read", "read", "xs:boolean", String.valueOf(read));
230 attributes.addAttribute(null, "write", "write", "xs:boolean", String.valueOf(write));
231 attributes.addAttribute(null, "execute", "execute", "xs:boolean", String.valueOf(execute));
232 contentHandler.startElement(null, name, name, attributes);
233 contentHandler.endElement(null, name, name);
234 contentHandler.lineBreak();
237 private void outputExtendedAttributes(File file) throws IOException, SAXException {
240 final List<ExtendedAttribute> extendedAttributes = getExtendedAttributes(file);
242 if (!extendedAttributes.isEmpty()) {
243 contentHandler.indentation(level);
244 contentHandler.startElement(null, XATTR_ELEMENT, XATTR_ELEMENT, null);
245 contentHandler.lineBreak();
247 for (ExtendedAttribute ea : extendedAttributes) {
248 contentHandler.indentation(level);
249 String elementName = encodeElementName(ea.getKey());
250 contentHandler.textElement(ea.getValue(), null, elementName, elementName, null);
251 contentHandler.lineBreak();
254 contentHandler.indentation(level);
255 contentHandler.endElement(null, XATTR_ELEMENT, XATTR_ELEMENT);
256 contentHandler.lineBreak();
262 private List<ExtendedAttribute> getExtendedAttributes(File file) throws IOException {
264 List<ExtendedAttribute> l = new ArrayList<>();
266 Path path = file.toPath();
267 FileSystemProvider provider = path.getFileSystem().provider();
268 UserDefinedFileAttributeView attributes = provider.getFileAttributeView(path, UserDefinedFileAttributeView.class);
270 for (String jménoAtributu : attributes.list()) {
271 ByteBuffer hodnotaAtributu = ByteBuffer.allocate(attributes.size(jménoAtributu));
272 attributes.read(jménoAtributu, hodnotaAtributu);
273 l.add(new ExtendedAttribute(jménoAtributu, hodnotaAtributu));