# HG changeset patch # User František Kučera # Date 1414544594 -3600 # Node ID 02739f60b1ec0351c43d347eb6e53be2c696e8e4 # Parent e62a3e4982121453512b14ea7201d7b7d546a1d2 in-fs: permissions and extended attributes diff -r e62a3e498212 -r 02739f60b1ec java/alt2xml-in-fs/src/cz/frantovo/alt2xml/in/fs/ExtendedAttribute.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/alt2xml-in-fs/src/cz/frantovo/alt2xml/in/fs/ExtendedAttribute.java Wed Oct 29 02:03:14 2014 +0100 @@ -0,0 +1,77 @@ +/** + * Alt2XML + * Copyright © 2014 František Kučera (frantovo.cz) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package cz.frantovo.alt2xml.in.fs; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +public class ExtendedAttribute { + + private String key; + private String value; + + public ExtendedAttribute(String key, String value) { + this.key = key; + this.value = value; + } + + public ExtendedAttribute(String key, ByteBuffer value) { + this.key = key; + setValue(value); + } + + public ExtendedAttribute() { + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public final ByteBuffer getValueBytes() { + return encode(getValue()); + } + + public void setValue(String value) { + this.value = value; + } + + public final void setValue(ByteBuffer value) { + setValue(decode(value)); + } + + private static String decode(ByteBuffer bytes) { + bytes.flip(); + return Charset.defaultCharset().decode(bytes).toString(); + } + + private static ByteBuffer encode(String text) { + if (text == null) { + return null; + } else { + return Charset.defaultCharset().encode(text); + } + } +} diff -r e62a3e498212 -r 02739f60b1ec java/alt2xml-in-fs/src/cz/frantovo/alt2xml/in/fs/Reader.java --- a/java/alt2xml-in-fs/src/cz/frantovo/alt2xml/in/fs/Reader.java Wed Oct 29 01:25:06 2014 +0100 +++ b/java/alt2xml-in-fs/src/cz/frantovo/alt2xml/in/fs/Reader.java Wed Oct 29 02:03:14 2014 +0100 @@ -22,6 +22,16 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.UserDefinedFileAttributeView; +import java.nio.file.spi.FileSystemProvider; +import java.util.List; +import java.util.ArrayList; +import java.util.Set; import java.util.logging.Logger; import org.xml.sax.Attributes; import org.xml.sax.InputSource; @@ -38,10 +48,21 @@ public static final String ROOT_ELEMENT = "fs"; public static final String DIR_ELEMENT = "dir"; public static final String FILE_ELEMENT = "file"; - public static final String ROOT_ATTRIBUTE = "root"; + public static final String XATTR_ELEMENT = "xattr"; + public static final String PERMISSIONS_ELEMENT = "mode"; + public static final String PERMISSIONS_U_ELEMENT = "user"; + public static final String PERMISSIONS_G_ELEMENT = "group"; + public static final String PERMISSIONS_O_ELEMENT = "others"; public static final String NAME_ATTRIBUTE = "name"; + public static final String ABSOLUTE_PATH_ATTRIBUTE = "path"; + public static final String SIZE_ATTRIBUTE = "size"; private static final Logger log = Logger.getLogger(Reader.class.getName()); + /** + * indentation level + */ + private int level = 0; + @Override public void parse(InputSource input) throws IOException, SAXException { File dir = getFile(input.getSystemId()); @@ -59,16 +80,38 @@ } } + private String getAbsolutePath(File file) throws IOException { + return file.getCanonicalFile().getAbsolutePath(); + } + + private Attributes singleAttribute(String name, int value) { + AttributesImpl attributes = new AttributesImpl(); + addAttribute(attributes, name, value); + return attributes; + } + private Attributes singleAttribute(String name, String value) { AttributesImpl attributes = new AttributesImpl(); - attributes.addAttribute(null, name, name, "xs:string", value); + addAttribute(attributes, name, value); return attributes; } - private void outputStart(File root) throws SAXException { + private void addAttribute(AttributesImpl attributes, String name, int value) { + attributes.addAttribute(null, name, name, "xs:int", String.valueOf(value)); + } + + private void addAttribute(AttributesImpl attributes, String name, long value) { + attributes.addAttribute(null, name, name, "xs:long", String.valueOf(value)); + } + + private void addAttribute(AttributesImpl attributes, String name, String value) { + attributes.addAttribute(null, name, name, "xs:string", value); + } + + private void outputStart(File root) throws SAXException, IOException { contentHandler.startDocument(); contentHandler.lineBreak(); - contentHandler.startElement(null, null, ROOT_ELEMENT, singleAttribute(ROOT_ATTRIBUTE, root.getAbsolutePath())); + contentHandler.startElement(null, null, ROOT_ELEMENT, singleAttribute(ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(root))); contentHandler.lineBreak(); } @@ -78,22 +121,46 @@ contentHandler.endDocument(); } - private void outputFile(File file) throws SAXException { - contentHandler.startElement(null, null, FILE_ELEMENT, singleAttribute(NAME_ATTRIBUTE, file.getName())); + private void outputFile(File file) throws SAXException, IOException { + level++; + + AttributesImpl attributes = new AttributesImpl(); + addAttribute(attributes, NAME_ATTRIBUTE, file.getName()); + addAttribute(attributes, ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(file)); + addAttribute(attributes, SIZE_ATTRIBUTE, file.length()); + + contentHandler.indentation(level); + contentHandler.startElement(null, null, FILE_ELEMENT, attributes); contentHandler.lineBreak(); - //contentHandler.characters(file.getName()); + outputPermissions(file.toPath()); + outputExtendedAttributes(file); + contentHandler.indentation(level); contentHandler.endElement(null, null, FILE_ELEMENT); contentHandler.lineBreak(); + + level--; } - private void outputDir(File dir) throws SAXException { + private void outputDir(File dir) throws SAXException, IOException { + level++; - contentHandler.startElement(null, null, DIR_ELEMENT, singleAttribute(NAME_ATTRIBUTE, dir.getName())); + final File[] children = dir.listFiles(); + + AttributesImpl attributes = new AttributesImpl(); + addAttribute(attributes, NAME_ATTRIBUTE, dir.getName()); + addAttribute(attributes, ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(dir)); + addAttribute(attributes, SIZE_ATTRIBUTE, children.length); + + contentHandler.indentation(level); + contentHandler.startElement(null, null, DIR_ELEMENT, attributes); contentHandler.lineBreak(); - for (File file : dir.listFiles()) { + outputPermissions(dir.toPath()); + outputExtendedAttributes(dir); + + for (File file : children) { if (file.isDirectory()) { outputDir(file); } else { @@ -101,7 +168,112 @@ } } + contentHandler.indentation(level); contentHandler.endElement(null, null, DIR_ELEMENT); contentHandler.lineBreak(); + + level--; + } + + private String encodeElementName(String originalName) { + /** + * TODO: encode and/or skip invalid characters + */ + return originalName; + } + + private void outputPermissions(Path path) throws IOException, SAXException { + level++; + Set permissions = Files.getPosixFilePermissions(path, LinkOption.NOFOLLOW_LINKS); + + contentHandler.indentation(level); + contentHandler.startElement(null, PERMISSIONS_ELEMENT, PERMISSIONS_ELEMENT, singleAttribute("octal", getOctal(permissions))); + contentHandler.lineBreak(); + + level++; + + outputPermission("owner", permissions.contains(PosixFilePermission.OWNER_READ), permissions.contains(PosixFilePermission.OWNER_WRITE), permissions.contains(PosixFilePermission.OWNER_EXECUTE)); + outputPermission("group", permissions.contains(PosixFilePermission.GROUP_READ), permissions.contains(PosixFilePermission.GROUP_WRITE), permissions.contains(PosixFilePermission.GROUP_EXECUTE)); + outputPermission("others", permissions.contains(PosixFilePermission.OTHERS_READ), permissions.contains(PosixFilePermission.OTHERS_WRITE), permissions.contains(PosixFilePermission.OTHERS_EXECUTE)); + + level--; + + contentHandler.indentation(level); + contentHandler.endElement(null, PERMISSIONS_ELEMENT, PERMISSIONS_ELEMENT); + contentHandler.lineBreak(); + + level--; + } + + private int getOctal(Set permissions) { + int octal = 0; + + octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_READ) ? 4 : 0); + octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_WRITE) ? 2 : 0); + octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_EXECUTE) ? 1 : 0); + + octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_READ) ? 4 : 0); + octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_WRITE) ? 2 : 0); + octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_EXECUTE) ? 1 : 0); + + octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_READ) ? 4 : 0); + octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_WRITE) ? 2 : 0); + octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_EXECUTE) ? 1 : 0); + + return octal; + } + + private void outputPermission(String name, boolean read, boolean write, boolean execute) throws SAXException { + contentHandler.indentation(level); + AttributesImpl attributes = new AttributesImpl(); + attributes.addAttribute(null, "read", "read", "xs:boolean", String.valueOf(read)); + attributes.addAttribute(null, "write", "write", "xs:boolean", String.valueOf(write)); + attributes.addAttribute(null, "execute", "execute", "xs:boolean", String.valueOf(execute)); + contentHandler.startElement(null, name, name, attributes); + contentHandler.endElement(null, name, name); + contentHandler.lineBreak(); + } + + private void outputExtendedAttributes(File file) throws IOException, SAXException { + level++; + + final List extendedAttributes = getExtendedAttributes(file); + + if (!extendedAttributes.isEmpty()) { + contentHandler.indentation(level); + contentHandler.startElement(null, XATTR_ELEMENT, XATTR_ELEMENT, null); + contentHandler.lineBreak(); + level++; + for (ExtendedAttribute ea : extendedAttributes) { + contentHandler.indentation(level); + String elementName = encodeElementName(ea.getKey()); + contentHandler.textElement(ea.getValue(), null, elementName, elementName, null); + contentHandler.lineBreak(); + } + level--; + contentHandler.indentation(level); + contentHandler.endElement(null, XATTR_ELEMENT, XATTR_ELEMENT); + contentHandler.lineBreak(); + } + + level--; + } + + private List getExtendedAttributes(File file) throws IOException { + + List l = new ArrayList<>(); + + Path path = file.toPath(); + FileSystemProvider provider = path.getFileSystem().provider(); + UserDefinedFileAttributeView attributes = provider.getFileAttributeView(path, UserDefinedFileAttributeView.class); + + for (String jménoAtributu : attributes.list()) { + ByteBuffer hodnotaAtributu = ByteBuffer.allocate(attributes.size(jménoAtributu)); + attributes.read(jménoAtributu, hodnotaAtributu); + l.add(new ExtendedAttribute(jménoAtributu, hodnotaAtributu)); + } + + return l; + } }