franta-hg@11: /** franta-hg@11: * Alt2XML franta-hg@11: * Copyright © 2014 František Kučera (frantovo.cz) franta-hg@11: * franta-hg@11: * This program is free software: you can redistribute it and/or modify franta-hg@11: * it under the terms of the GNU General Public License as published by franta-hg@111: * the Free Software Foundation, version 3 of the License. franta-hg@11: * franta-hg@11: * This program is distributed in the hope that it will be useful, franta-hg@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of franta-hg@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the franta-hg@11: * GNU General Public License for more details. franta-hg@11: * franta-hg@11: * You should have received a copy of the GNU General Public License franta-hg@11: * along with this program. If not, see . franta-hg@11: */ franta-hg@103: package cz.frantovo.alt2xml.in.fs; franta-hg@2: franta-hg@17: import cz.frantovo.alt2xml.AbstractAlt2XmlReader; franta-hg@103: import java.io.File; franta-hg@2: import java.io.IOException; franta-hg@103: import java.net.URI; franta-hg@103: import java.net.URISyntaxException; franta-hg@106: import java.nio.ByteBuffer; franta-hg@106: import java.nio.file.Files; franta-hg@106: import java.nio.file.LinkOption; franta-hg@106: import java.nio.file.Path; franta-hg@106: import java.nio.file.attribute.PosixFilePermission; franta-hg@106: import java.nio.file.attribute.UserDefinedFileAttributeView; franta-hg@106: import java.nio.file.spi.FileSystemProvider; franta-hg@106: import java.util.List; franta-hg@106: import java.util.ArrayList; franta-hg@106: import java.util.Set; franta-hg@77: import java.util.logging.Logger; franta-hg@103: import org.xml.sax.Attributes; franta-hg@2: import org.xml.sax.InputSource; franta-hg@2: import org.xml.sax.SAXException; franta-hg@92: import org.xml.sax.helpers.AttributesImpl; franta-hg@2: franta-hg@2: /** franta-hg@103: * Reads filesystem – hierarchical structure of directories and files. franta-hg@2: * franta-hg@17: * @author Ing. František Kučera (frantovo.cz) franta-hg@2: */ franta-hg@17: public class Reader extends AbstractAlt2XmlReader { franta-hg@13: franta-hg@103: public static final String ROOT_ELEMENT = "fs"; franta-hg@103: public static final String DIR_ELEMENT = "dir"; franta-hg@103: public static final String FILE_ELEMENT = "file"; franta-hg@106: public static final String XATTR_ELEMENT = "xattr"; franta-hg@106: public static final String PERMISSIONS_ELEMENT = "mode"; franta-hg@106: public static final String PERMISSIONS_U_ELEMENT = "user"; franta-hg@106: public static final String PERMISSIONS_G_ELEMENT = "group"; franta-hg@106: public static final String PERMISSIONS_O_ELEMENT = "others"; franta-hg@103: public static final String NAME_ATTRIBUTE = "name"; franta-hg@106: public static final String ABSOLUTE_PATH_ATTRIBUTE = "path"; franta-hg@106: public static final String SIZE_ATTRIBUTE = "size"; franta-hg@77: private static final Logger log = Logger.getLogger(Reader.class.getName()); franta-hg@77: franta-hg@106: /** franta-hg@106: * indentation level franta-hg@106: */ franta-hg@106: private int level = 0; franta-hg@106: franta-hg@2: @Override franta-hg@6: public void parse(InputSource input) throws IOException, SAXException { franta-hg@103: File dir = getFile(input.getSystemId()); franta-hg@77: franta-hg@103: outputStart(dir); franta-hg@103: outputDir(dir); franta-hg@59: outputEnd(); franta-hg@59: } franta-hg@59: franta-hg@103: private File getFile(String systemId) throws IOException { franta-hg@103: try { franta-hg@103: return new File(new URI(systemId)); franta-hg@103: } catch (URISyntaxException e) { franta-hg@103: throw new IOException("Invalid dir URI", e); franta-hg@103: } franta-hg@103: } franta-hg@103: franta-hg@106: private String getAbsolutePath(File file) throws IOException { franta-hg@106: return file.getCanonicalFile().getAbsolutePath(); franta-hg@106: } franta-hg@106: franta-hg@106: private Attributes singleAttribute(String name, int value) { franta-hg@106: AttributesImpl attributes = new AttributesImpl(); franta-hg@106: addAttribute(attributes, name, value); franta-hg@106: return attributes; franta-hg@106: } franta-hg@109: franta-hg@103: private Attributes singleAttribute(String name, String value) { franta-hg@103: AttributesImpl attributes = new AttributesImpl(); franta-hg@106: addAttribute(attributes, name, value); franta-hg@103: return attributes; franta-hg@103: } franta-hg@103: franta-hg@106: private void addAttribute(AttributesImpl attributes, String name, int value) { franta-hg@106: attributes.addAttribute(null, name, name, "xs:int", String.valueOf(value)); franta-hg@106: } franta-hg@106: franta-hg@106: private void addAttribute(AttributesImpl attributes, String name, long value) { franta-hg@106: attributes.addAttribute(null, name, name, "xs:long", String.valueOf(value)); franta-hg@106: } franta-hg@106: franta-hg@106: private void addAttribute(AttributesImpl attributes, String name, String value) { franta-hg@106: attributes.addAttribute(null, name, name, "xs:string", value); franta-hg@106: } franta-hg@106: franta-hg@106: private void outputStart(File root) throws SAXException, IOException { franta-hg@21: contentHandler.startDocument(); franta-hg@76: contentHandler.lineBreak(); franta-hg@106: contentHandler.startElement(null, null, ROOT_ELEMENT, singleAttribute(ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(root))); franta-hg@75: contentHandler.lineBreak(); franta-hg@59: } franta-hg@59: franta-hg@59: private void outputEnd() throws SAXException { franta-hg@102: contentHandler.endElement(null, null, ROOT_ELEMENT); franta-hg@75: contentHandler.lineBreak(); franta-hg@21: contentHandler.endDocument(); franta-hg@6: } franta-hg@76: franta-hg@106: private void outputFile(File file) throws SAXException, IOException { franta-hg@106: level++; franta-hg@106: franta-hg@106: AttributesImpl attributes = new AttributesImpl(); franta-hg@106: addAttribute(attributes, NAME_ATTRIBUTE, file.getName()); franta-hg@106: addAttribute(attributes, ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(file)); franta-hg@106: addAttribute(attributes, SIZE_ATTRIBUTE, file.length()); franta-hg@106: franta-hg@106: contentHandler.indentation(level); franta-hg@106: contentHandler.startElement(null, null, FILE_ELEMENT, attributes); franta-hg@103: contentHandler.lineBreak(); franta-hg@77: franta-hg@106: outputPermissions(file.toPath()); franta-hg@106: outputExtendedAttributes(file); franta-hg@77: franta-hg@106: contentHandler.indentation(level); franta-hg@103: contentHandler.endElement(null, null, FILE_ELEMENT); franta-hg@103: contentHandler.lineBreak(); franta-hg@106: franta-hg@106: level--; franta-hg@77: } franta-hg@77: franta-hg@106: private void outputDir(File dir) throws SAXException, IOException { franta-hg@106: level++; franta-hg@89: franta-hg@106: final File[] children = dir.listFiles(); franta-hg@106: franta-hg@106: AttributesImpl attributes = new AttributesImpl(); franta-hg@106: addAttribute(attributes, NAME_ATTRIBUTE, dir.getName()); franta-hg@106: addAttribute(attributes, ABSOLUTE_PATH_ATTRIBUTE, getAbsolutePath(dir)); franta-hg@106: addAttribute(attributes, SIZE_ATTRIBUTE, children.length); franta-hg@106: franta-hg@106: contentHandler.indentation(level); franta-hg@106: contentHandler.startElement(null, null, DIR_ELEMENT, attributes); franta-hg@103: contentHandler.lineBreak(); franta-hg@77: franta-hg@106: outputPermissions(dir.toPath()); franta-hg@106: outputExtendedAttributes(dir); franta-hg@106: franta-hg@106: for (File file : children) { franta-hg@103: if (file.isDirectory()) { franta-hg@103: outputDir(file); franta-hg@103: } else { franta-hg@103: outputFile(file); franta-hg@81: } franta-hg@77: } franta-hg@77: franta-hg@106: contentHandler.indentation(level); franta-hg@103: contentHandler.endElement(null, null, DIR_ELEMENT); franta-hg@103: contentHandler.lineBreak(); franta-hg@106: franta-hg@106: level--; franta-hg@106: } franta-hg@106: franta-hg@106: private String encodeElementName(String originalName) { franta-hg@106: /** franta-hg@106: * TODO: encode and/or skip invalid characters franta-hg@106: */ franta-hg@106: return originalName; franta-hg@106: } franta-hg@106: franta-hg@106: private void outputPermissions(Path path) throws IOException, SAXException { franta-hg@106: level++; franta-hg@109: Set permissions = Files.getPosixFilePermissions(path); // TODO: maybe symlinks: LinkOption.NOFOLLOW_LINKS franta-hg@106: franta-hg@106: contentHandler.indentation(level); franta-hg@106: contentHandler.startElement(null, PERMISSIONS_ELEMENT, PERMISSIONS_ELEMENT, singleAttribute("octal", getOctal(permissions))); franta-hg@106: contentHandler.lineBreak(); franta-hg@106: franta-hg@106: level++; franta-hg@106: franta-hg@106: outputPermission("owner", permissions.contains(PosixFilePermission.OWNER_READ), permissions.contains(PosixFilePermission.OWNER_WRITE), permissions.contains(PosixFilePermission.OWNER_EXECUTE)); franta-hg@106: outputPermission("group", permissions.contains(PosixFilePermission.GROUP_READ), permissions.contains(PosixFilePermission.GROUP_WRITE), permissions.contains(PosixFilePermission.GROUP_EXECUTE)); franta-hg@106: outputPermission("others", permissions.contains(PosixFilePermission.OTHERS_READ), permissions.contains(PosixFilePermission.OTHERS_WRITE), permissions.contains(PosixFilePermission.OTHERS_EXECUTE)); franta-hg@106: franta-hg@106: level--; franta-hg@106: franta-hg@106: contentHandler.indentation(level); franta-hg@106: contentHandler.endElement(null, PERMISSIONS_ELEMENT, PERMISSIONS_ELEMENT); franta-hg@106: contentHandler.lineBreak(); franta-hg@106: franta-hg@106: level--; franta-hg@106: } franta-hg@106: franta-hg@106: private int getOctal(Set permissions) { franta-hg@106: int octal = 0; franta-hg@109: franta-hg@106: octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_READ) ? 4 : 0); franta-hg@106: octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_WRITE) ? 2 : 0); franta-hg@106: octal = octal + 100 * (permissions.contains(PosixFilePermission.OWNER_EXECUTE) ? 1 : 0); franta-hg@109: franta-hg@106: octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_READ) ? 4 : 0); franta-hg@106: octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_WRITE) ? 2 : 0); franta-hg@106: octal = octal + 10 * (permissions.contains(PosixFilePermission.GROUP_EXECUTE) ? 1 : 0); franta-hg@109: franta-hg@106: octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_READ) ? 4 : 0); franta-hg@106: octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_WRITE) ? 2 : 0); franta-hg@106: octal = octal + 1 * (permissions.contains(PosixFilePermission.OTHERS_EXECUTE) ? 1 : 0); franta-hg@106: franta-hg@106: return octal; franta-hg@106: } franta-hg@106: franta-hg@106: private void outputPermission(String name, boolean read, boolean write, boolean execute) throws SAXException { franta-hg@106: contentHandler.indentation(level); franta-hg@106: AttributesImpl attributes = new AttributesImpl(); franta-hg@106: attributes.addAttribute(null, "read", "read", "xs:boolean", String.valueOf(read)); franta-hg@106: attributes.addAttribute(null, "write", "write", "xs:boolean", String.valueOf(write)); franta-hg@106: attributes.addAttribute(null, "execute", "execute", "xs:boolean", String.valueOf(execute)); franta-hg@106: contentHandler.startElement(null, name, name, attributes); franta-hg@106: contentHandler.endElement(null, name, name); franta-hg@106: contentHandler.lineBreak(); franta-hg@106: } franta-hg@106: franta-hg@106: private void outputExtendedAttributes(File file) throws IOException, SAXException { franta-hg@106: level++; franta-hg@106: franta-hg@106: final List extendedAttributes = getExtendedAttributes(file); franta-hg@106: franta-hg@106: if (!extendedAttributes.isEmpty()) { franta-hg@106: contentHandler.indentation(level); franta-hg@106: contentHandler.startElement(null, XATTR_ELEMENT, XATTR_ELEMENT, null); franta-hg@106: contentHandler.lineBreak(); franta-hg@106: level++; franta-hg@106: for (ExtendedAttribute ea : extendedAttributes) { franta-hg@106: contentHandler.indentation(level); franta-hg@106: String elementName = encodeElementName(ea.getKey()); franta-hg@106: contentHandler.textElement(ea.getValue(), null, elementName, elementName, null); franta-hg@106: contentHandler.lineBreak(); franta-hg@106: } franta-hg@106: level--; franta-hg@106: contentHandler.indentation(level); franta-hg@106: contentHandler.endElement(null, XATTR_ELEMENT, XATTR_ELEMENT); franta-hg@106: contentHandler.lineBreak(); franta-hg@106: } franta-hg@106: franta-hg@106: level--; franta-hg@106: } franta-hg@106: franta-hg@106: private List getExtendedAttributes(File file) throws IOException { franta-hg@106: franta-hg@106: List l = new ArrayList<>(); franta-hg@106: franta-hg@106: Path path = file.toPath(); franta-hg@106: FileSystemProvider provider = path.getFileSystem().provider(); franta-hg@106: UserDefinedFileAttributeView attributes = provider.getFileAttributeView(path, UserDefinedFileAttributeView.class); franta-hg@106: franta-hg@106: for (String jménoAtributu : attributes.list()) { franta-hg@106: ByteBuffer hodnotaAtributu = ByteBuffer.allocate(attributes.size(jménoAtributu)); franta-hg@106: attributes.read(jménoAtributu, hodnotaAtributu); franta-hg@106: l.add(new ExtendedAttribute(jménoAtributu, hodnotaAtributu)); franta-hg@106: } franta-hg@106: franta-hg@106: return l; franta-hg@106: franta-hg@77: } franta-hg@2: }