1/*
2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package jdk.internal.jrtfs;
26
27import java.nio.file.LinkOption;
28import java.nio.file.attribute.*;
29import java.io.IOException;
30import java.util.LinkedHashMap;
31import java.util.Map;
32import java.util.Objects;
33
34/**
35 * File attribute view for jrt file system.
36 *
37 * @implNote This class needs to maintain JDK 8 source compatibility.
38 *
39 * It is used internally in the JDK to implement jimage/jrtfs access,
40 * but also compiled and delivered as part of the jrtfs.jar to support access
41 * to the jimage file provided by the shipped JDK by tools running on JDK 8.
42 */
43final class JrtFileAttributeView implements BasicFileAttributeView {
44
45    private static enum AttrID {
46        size,
47        creationTime,
48        lastAccessTime,
49        lastModifiedTime,
50        isDirectory,
51        isRegularFile,
52        isSymbolicLink,
53        isOther,
54        fileKey,
55        compressedSize,
56        extension
57    };
58
59    private final JrtPath path;
60    private final boolean isJrtView;
61    private final LinkOption[] options;
62
63    private JrtFileAttributeView(JrtPath path, boolean isJrtView, LinkOption... options) {
64        this.path = path;
65        this.isJrtView = isJrtView;
66        this.options = options;
67    }
68
69    @SuppressWarnings("unchecked") // Cast to V
70    static <V extends FileAttributeView> V get(JrtPath path, Class<V> type, LinkOption... options) {
71        Objects.requireNonNull(type);
72        if (type == BasicFileAttributeView.class) {
73            return (V) new JrtFileAttributeView(path, false, options);
74        }
75        if (type == JrtFileAttributeView.class) {
76            return (V) new JrtFileAttributeView(path, true, options);
77        }
78        return null;
79    }
80
81    static JrtFileAttributeView get(JrtPath path, String type, LinkOption... options) {
82        Objects.requireNonNull(type);
83        if (type.equals("basic")) {
84            return new JrtFileAttributeView(path, false, options);
85        }
86        if (type.equals("jrt")) {
87            return new JrtFileAttributeView(path, true, options);
88        }
89        return null;
90    }
91
92    @Override
93    public String name() {
94        return isJrtView ? "jrt" : "basic";
95    }
96
97    @Override
98    public JrtFileAttributes readAttributes() throws IOException {
99        return path.getAttributes(options);
100    }
101
102    @Override
103    public void setTimes(FileTime lastModifiedTime,
104                         FileTime lastAccessTime,
105                         FileTime createTime) throws IOException {
106        path.setTimes(lastModifiedTime, lastAccessTime, createTime);
107    }
108
109    static void setAttribute(JrtPath path, String attribute, Object value)
110            throws IOException {
111        int colonPos = attribute.indexOf(':');
112        if (colonPos != -1) {    // type = "basic", if no ":"
113            String type = attribute.substring(0, colonPos++);
114            if (!type.equals("basic") && !type.equals("jrt")) {
115                throw new UnsupportedOperationException(
116                    "view <" + type + "> is not supported");
117            }
118            attribute = attribute.substring(colonPos);
119        }
120        try {
121            AttrID id = AttrID.valueOf(attribute);
122            if (id == AttrID.lastModifiedTime) {
123                path.setTimes((FileTime) value, null, null);
124            } else if (id == AttrID.lastAccessTime) {
125                path.setTimes(null, (FileTime) value, null);
126            } else if (id == AttrID.creationTime) {
127                path.setTimes(null, null, (FileTime) value);
128            }
129            return;
130        } catch (IllegalArgumentException x) {}
131        throw new UnsupportedOperationException("'" + attribute
132                + "' is unknown or read-only attribute");
133    }
134
135    static Map<String, Object> readAttributes(JrtPath path, String attributes,
136                                              LinkOption... options)
137            throws IOException {
138        int colonPos = attributes.indexOf(':');
139        boolean isJrtView = false;
140        if (colonPos != -1) {    // type = "basic", if no ":"
141            String type = attributes.substring(0, colonPos++);
142            if (!type.equals("basic") && !type.equals("jrt")) {
143                throw new UnsupportedOperationException("view <" + type +
144                                                        "> is not supported");
145            }
146            isJrtView = true;
147            attributes = attributes.substring(colonPos);
148        }
149        JrtFileAttributes jrtfas = path.getAttributes();
150        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
151        if ("*".equals(attributes)) {
152            for (AttrID id : AttrID.values()) {
153                map.put(id.name(), attribute(id, jrtfas, isJrtView));
154            }
155        } else {
156            String[] as = attributes.split(",");
157            for (String a : as) {
158                //throw IllegalArgumentException
159                map.put(a, attribute(AttrID.valueOf(a), jrtfas, isJrtView));
160            }
161        }
162        return map;
163    }
164
165    static Object attribute(AttrID id, JrtFileAttributes jrtfas, boolean isJrtView) {
166        switch (id) {
167            case size:
168                return jrtfas.size();
169            case creationTime:
170                return jrtfas.creationTime();
171            case lastAccessTime:
172                return jrtfas.lastAccessTime();
173            case lastModifiedTime:
174                return jrtfas.lastModifiedTime();
175            case isDirectory:
176                return jrtfas.isDirectory();
177            case isRegularFile:
178                return jrtfas.isRegularFile();
179            case isSymbolicLink:
180                return jrtfas.isSymbolicLink();
181            case isOther:
182                return jrtfas.isOther();
183            case fileKey:
184                return jrtfas.fileKey();
185            case compressedSize:
186                if (isJrtView) {
187                    return jrtfas.compressedSize();
188                }
189                break;
190            case extension:
191                if (isJrtView) {
192                    return jrtfas.extension();
193                }
194                break;
195        }
196        return null;
197    }
198}
199