| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.openapi.vfs.impl; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.io.FileAttributes; |
| import com.intellij.openapi.util.io.FileSystemUtil; |
| import com.intellij.reference.SoftReference; |
| import com.intellij.util.ArrayUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.ref.Reference; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| public abstract class ArchiveHandler { |
| public static final long DEFAULT_LENGTH = 0L; |
| public static final long DEFAULT_TIMESTAMP = -1L; |
| |
| protected static class EntryInfo { |
| public final EntryInfo parent; |
| public final String shortName; |
| public final boolean isDirectory; |
| public final long length; |
| public final long timestamp; |
| |
| public EntryInfo(EntryInfo parent, @NotNull String shortName, boolean isDirectory, long length, long timestamp) { |
| this.parent = parent; |
| this.shortName = shortName; |
| this.isDirectory = isDirectory; |
| this.length = length; |
| this.timestamp = timestamp; |
| } |
| } |
| |
| private final String myPath; |
| private final Object myLock = new Object(); |
| private volatile Reference<Map<String, EntryInfo>> myEntries = new SoftReference<Map<String, EntryInfo>>(null); |
| private boolean myCorrupted = false; |
| |
| protected ArchiveHandler(@NotNull String path) { |
| myPath = path; |
| } |
| |
| @NotNull |
| public File getFile() { |
| return new File(myPath); |
| } |
| |
| @Nullable |
| public FileAttributes getAttributes(@NotNull String relativePath) { |
| if (relativePath.isEmpty()) { |
| FileAttributes attributes = FileSystemUtil.getAttributes(myPath); |
| return attributes != null ? new FileAttributes(true, false, false, false, DEFAULT_LENGTH, DEFAULT_TIMESTAMP, false) : null; |
| } |
| else { |
| EntryInfo entry = getEntryInfo(relativePath); |
| return entry != null ? new FileAttributes(entry.isDirectory, false, false, false, entry.length, entry.timestamp, false) : null; |
| } |
| } |
| |
| @NotNull |
| public String[] list(@NotNull String relativePath) { |
| EntryInfo entry = getEntryInfo(relativePath); |
| if (entry == null || !entry.isDirectory) return ArrayUtil.EMPTY_STRING_ARRAY; |
| |
| Set<String> names = new HashSet<String>(); |
| for (EntryInfo info : getEntriesMap().values()) { |
| if (info.parent == entry) { |
| names.add(info.shortName); |
| } |
| } |
| return ArrayUtil.toStringArray(names); |
| } |
| |
| @Nullable |
| protected EntryInfo getEntryInfo(@NotNull String relativePath) { |
| return getEntriesMap().get(relativePath); |
| } |
| |
| @NotNull |
| protected Map<String, EntryInfo> getEntriesMap() { |
| Map<String, EntryInfo> map = SoftReference.dereference(myEntries); |
| if (map == null) { |
| synchronized (myLock) { |
| map = SoftReference.dereference(myEntries); |
| |
| if (map == null) { |
| if (myCorrupted) { |
| map = Collections.emptyMap(); |
| } |
| else { |
| try { |
| map = Collections.unmodifiableMap(createEntriesMap()); |
| } |
| catch (IOException e) { |
| myCorrupted = true; |
| Logger.getInstance(getClass()).warn(e.getMessage() + ": " + myPath, e); |
| map = Collections.emptyMap(); |
| } |
| } |
| |
| myEntries = new SoftReference<Map<String, EntryInfo>>(map); |
| } |
| } |
| } |
| return map; |
| } |
| |
| @NotNull |
| protected abstract Map<String, EntryInfo> createEntriesMap() throws IOException; |
| |
| @NotNull |
| protected EntryInfo createRootEntry() { |
| return new EntryInfo(null, "", true, DEFAULT_LENGTH, DEFAULT_TIMESTAMP); |
| } |
| |
| @NotNull |
| protected EntryInfo getOrCreate(@NotNull Map<String, EntryInfo> map, @NotNull String entryName) { |
| EntryInfo entry = map.get(entryName); |
| if (entry == null) { |
| Pair<String, String> path = splitPath(entryName); |
| EntryInfo parentEntry = getOrCreate(map, path.first); |
| entry = new EntryInfo(parentEntry, path.second, true, DEFAULT_LENGTH, DEFAULT_TIMESTAMP); |
| map.put(entryName, entry); |
| } |
| return entry; |
| } |
| |
| @NotNull |
| protected Pair<String, String> splitPath(@NotNull String entryName) { |
| int p = entryName.lastIndexOf('/'); |
| String parentName = p > 0 ? entryName.substring(0, p) : ""; |
| String shortName = p > 0 ? entryName.substring(p + 1) : entryName; |
| return Pair.create(parentName, shortName); |
| } |
| |
| @NotNull |
| public abstract byte[] contentsToByteArray(@NotNull String relativePath) throws IOException; |
| } |