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, version 3 of the License.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 package cz.frantovo.alt2xml.in.fs;
19 import cz.frantovo.alt2xml.AbstractAlt2XmlReader;
21 import java.io.IOException;
23 import java.net.URISyntaxException;
24 import java.nio.ByteBuffer;
25 import java.nio.file.Files;
26 import java.nio.file.LinkOption;
27 import java.nio.file.Path;
28 import java.nio.file.attribute.PosixFilePermission;
29 import java.nio.file.attribute.UserDefinedFileAttributeView;
30 import java.nio.file.spi.FileSystemProvider;
31 import java.util.List;
32 import java.util.ArrayList;
34 import java.util.logging.Logger;
35 import org.xml.sax.Attributes;
36 import org.xml.sax.InputSource;
37 import org.xml.sax.SAXException;
38 import org.xml.sax.helpers.AttributesImpl;
41 * Reads filesystem – hierarchical structure of directories and files.
43 * @author Ing. František Kučera (frantovo.cz)
45 public class Reader extends AbstractAlt2XmlReader {
47 public static final String ROOT_ELEMENT = "fs";
48 public static final String DIR_ELEMENT = "dir";
49 public static final String FILE_ELEMENT = "file";
50 public static final String XATTR_ELEMENT = "xattr";
51 public static final String PERMISSIONS_ELEMENT = "mode";
52 public static final String PERMISSIONS_U_ELEMENT = "user";
53 public static final String PERMISSIONS_G_ELEMENT = "group";
54 public static final String PERMISSIONS_O_ELEMENT = "others";
55 public static final String NAME_ATTRIBUTE = "name";
56 public static final String ABSOLUTE_PATH_ATTRIBUTE = "path";
57 public static final String SIZE_ATTRIBUTE = "size";
58 private static final Logger log = Logger.getLogger(Reader.class.getName());
63 private int level = 0;
66 public void parse(InputSource input) throws IOException, SAXException {
67 File dir = getFile(input.getSystemId());
74 private File getFile(String systemId) throws IOException {
76 return new File(new URI(systemId));
77 } catch (URISyntaxException e) {
78 throw new IOException("Invalid dir URI", e);
82 private String getAbsolutePath(File file) throws IOException {
83 return file.getCanonicalFile().getAbsolutePath();
86 private Attributes singleAttribute(String name, int value) {
87 AttributesImpl attributes = new AttributesImpl();
88 addAttribute(attributes, name, value);
92 private Attributes singleAttribute(String name, String value) {
93 AttributesImpl attributes = new AttributesImpl();
94 addAttribute(attributes, name, value);
98 private void addAttribute(AttributesImpl attributes, String name, int value) {
99 attributes.addAttribute(null, name, name, "xs:int", String.valueOf(value));
102 private void addAttribute(AttributesImpl attributes, String name, long value) {
103 attributes.addAttribute(null, name, name, "xs:long", String.valueOf(value));
106 private void addAttribute(AttributesImpl attributes, String name, String value) {
107 attributes.addAttribute(null, name, name, "xs:string", value);
110 private void outputStart(File root) throws SAXException, IOException {
111 contentHandler.startDocument();
112 contentHandler.lineBreak();
113 contentHandler.startElement(null, null, ROOT_ELEMENT, singleAttribute(ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(root)));
114 contentHandler.lineBreak();
117 private void outputEnd() throws SAXException {
118 contentHandler.endElement(null, null, ROOT_ELEMENT);
119 contentHandler.lineBreak();
120 contentHandler.endDocument();
123 private void outputFile(File file) throws SAXException, IOException {
126 AttributesImpl attributes = new AttributesImpl();
127 addAttribute(attributes, NAME_ATTRIBUTE, file.getName());
128 addAttribute(attributes, ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(file));
129 addAttribute(attributes, SIZE_ATTRIBUTE, file.length());
131 contentHandler.indentation(level);
132 contentHandler.startElement(null, null, FILE_ELEMENT, attributes);
133 contentHandler.lineBreak();
135 outputPermissions(file.toPath());
136 outputExtendedAttributes(file);
138 contentHandler.indentation(level);
139 contentHandler.endElement(null, null, FILE_ELEMENT);
140 contentHandler.lineBreak();
145 private void outputDir(File dir) throws SAXException, IOException {
148 final File[] children = dir.listFiles();
150 AttributesImpl attributes = new AttributesImpl();
151 addAttribute(attributes, NAME_ATTRIBUTE, dir.getName());
152 addAttribute(attributes, ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(dir));
153 addAttribute(attributes, SIZE_ATTRIBUTE, children.length);
155 contentHandler.indentation(level);
156 contentHandler.startElement(null, null, DIR_ELEMENT, attributes);
157 contentHandler.lineBreak();
159 outputPermissions(dir.toPath());
160 outputExtendedAttributes(dir);
162 for (File file : children) {
163 if (file.isDirectory()) {
170 contentHandler.indentation(level);
171 contentHandler.endElement(null, null, DIR_ELEMENT);
172 contentHandler.lineBreak();
177 private String encodeElementName(String originalName) {
179 * TODO: encode and/or skip invalid characters
184 private void outputPermissions(Path path) throws IOException, SAXException {
186 Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path); // TODO: maybe symlinks: LinkOption.NOFOLLOW_LINKS
188 contentHandler.indentation(level);
189 contentHandler.startElement(null, PERMISSIONS_ELEMENT, PERMISSIONS_ELEMENT, singleAttribute("octal", getOctal(permissions)));
190 contentHandler.lineBreak();
194 outputPermission("owner", permissions.contains(PosixFilePermission.OWNER_READ), permissions.contains(PosixFilePermission.OWNER_WRITE), permissions.contains(PosixFilePermission.OWNER_EXECUTE));
195 outputPermission("group", permissions.contains(PosixFilePermission.GROUP_READ), permissions.contains(PosixFilePermission.GROUP_WRITE), permissions.contains(PosixFilePermission.GROUP_EXECUTE));
196 outputPermission("others", permissions.contains(PosixFilePermission.OTHERS_READ), permissions.contains(PosixFilePermission.OTHERS_WRITE), permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
200 contentHandler.indentation(level);
201 contentHandler.endElement(null, PERMISSIONS_ELEMENT, PERMISSIONS_ELEMENT);
202 contentHandler.lineBreak();
207 private int getOctal(Set<PosixFilePermission> permissions) {
210 octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_READ) ? 4 : 0);
211 octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_WRITE) ? 2 : 0);
212 octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_EXECUTE) ? 1 : 0);
214 octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_READ) ? 4 : 0);
215 octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_WRITE) ? 2 : 0);
216 octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_EXECUTE) ? 1 : 0);
218 octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_READ) ? 4 : 0);
219 octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_WRITE) ? 2 : 0);
220 octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_EXECUTE) ? 1 : 0);
225 private void outputPermission(String name, boolean read, boolean write, boolean execute) throws SAXException {
226 contentHandler.indentation(level);
227 AttributesImpl attributes = new AttributesImpl();
228 attributes.addAttribute(null, "read", "read", "xs:boolean", String.valueOf(read));
229 attributes.addAttribute(null, "write", "write", "xs:boolean", String.valueOf(write));
230 attributes.addAttribute(null, "execute", "execute", "xs:boolean", String.valueOf(execute));
231 contentHandler.startElement(null, name, name, attributes);
232 contentHandler.endElement(null, name, name);
233 contentHandler.lineBreak();
236 private void outputExtendedAttributes(File file) throws IOException, SAXException {
239 final List<ExtendedAttribute> extendedAttributes = getExtendedAttributes(file);
241 if (!extendedAttributes.isEmpty()) {
242 contentHandler.indentation(level);
243 contentHandler.startElement(null, XATTR_ELEMENT, XATTR_ELEMENT, null);
244 contentHandler.lineBreak();
246 for (ExtendedAttribute ea : extendedAttributes) {
247 contentHandler.indentation(level);
248 String elementName = encodeElementName(ea.getKey());
249 contentHandler.textElement(ea.getValue(), null, elementName, elementName, null);
250 contentHandler.lineBreak();
253 contentHandler.indentation(level);
254 contentHandler.endElement(null, XATTR_ELEMENT, XATTR_ELEMENT);
255 contentHandler.lineBreak();
261 private List<ExtendedAttribute> getExtendedAttributes(File file) throws IOException {
263 List<ExtendedAttribute> l = new ArrayList<>();
265 Path path = file.toPath();
266 FileSystemProvider provider = path.getFileSystem().provider();
267 UserDefinedFileAttributeView attributes = provider.getFileAttributeView(path, UserDefinedFileAttributeView.class);
269 for (String jménoAtributu : attributes.list()) {
270 ByteBuffer hodnotaAtributu = ByteBuffer.allocate(attributes.size(jménoAtributu));
271 attributes.read(jménoAtributu, hodnotaAtributu);
272 l.add(new ExtendedAttribute(jménoAtributu, hodnotaAtributu));