/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.twill.filesystem; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.util.Collections; import java.util.Deque; import java.util.List; import java.util.UUID; /** * A concrete implementation of {@link Location} for the Local filesystem. */ final class LocalLocation implements Location { private final File file; /** * Constructs a LocalLocation. * * @param file to the file. */ LocalLocation(File file) { this.file = file; } /** * Checks if the this location exists on local file system. * * @return true if found; false otherwise. * @throws java.io.IOException */ @Override public boolean exists() throws IOException { return file.exists(); } /** * @return An {@link java.io.InputStream} for this location on local filesystem. * @throws IOException */ @Override public InputStream getInputStream() throws IOException { File parent = file.getParentFile(); if (!parent.exists()) { parent.mkdirs(); } return new FileInputStream(file); } /** * @return An {@link java.io.OutputStream} for this location on local filesystem. * @throws IOException */ @Override public OutputStream getOutputStream() throws IOException { File parent = file.getParentFile(); if (!parent.exists()) { parent.mkdirs(); } return new FileOutputStream(file); } /** * Local location doesn't supports permission. It's the same as calling {@link #getOutputStream()}. */ @Override public OutputStream getOutputStream(String permission) throws IOException { return getOutputStream(); } /** * @return Returns the name of the file or directory denoteed by this abstract pathname. */ @Override public String getName() { return file.getName(); } @Override public boolean createNew() throws IOException { return file.createNewFile(); } /** * Appends the child to the current {@link Location} on local filesystem. * <p> * Returns a new instance of Location. * </p> * * @param child to be appended to this location. * @return A new instance of {@link Location} * @throws IOException */ @Override public Location append(String child) throws IOException { return new LocalLocation(new File(file, child)); } @Override public Location getTempFile(String suffix) throws IOException { return new LocalLocation( new File(file.getAbsolutePath() + "." + UUID.randomUUID() + (suffix == null ? TEMP_FILE_SUFFIX : suffix))); } /** * @return A {@link URI} for this location on local filesystem. */ @Override public URI toURI() { return file.toURI(); } /** * Deletes the file or directory denoted by this abstract pathname. If this * pathname denotes a directory, then the directory must be empty in order * to be deleted. * * @return true if and only if the file or directory is successfully delete; false otherwise. */ @Override public boolean delete() throws IOException { return file.delete(); } @Override public boolean delete(boolean recursive) throws IOException { if (!recursive) { return delete(); } Deque<File> stack = Lists.newLinkedList(); stack.add(file); while (!stack.isEmpty()) { File f = stack.peekLast(); File[] files = f.listFiles(); if (files != null && files.length != 0) { Collections.addAll(stack, files); } else { if (!f.delete()) { return false; } stack.pollLast(); } } return true; } @Override public Location renameTo(Location destination) throws IOException { // destination will always be of the same type as this location boolean success = file.renameTo(((LocalLocation) destination).file); if (success) { return new LocalLocation(((LocalLocation) destination).file); } else { return null; } } /** * Creates the directory named by this abstract pathname, including any necessary * but nonexistent parent directories. * * @return true if and only if the renaming succeeded; false otherwise */ @Override public boolean mkdirs() throws IOException { return file.mkdirs(); } /** * @return Length of file. */ @Override public long length() throws IOException { return file.length(); } @Override public long lastModified() { return file.lastModified(); } @Override public boolean isDirectory() throws IOException { return file.isDirectory(); } @Override public List<Location> list() throws IOException { File[] files = file.listFiles(); ImmutableList.Builder<Location> result = ImmutableList.builder(); if (files != null) { for (File file : files) { result.add(new LocalLocation(file)); } } else if (!file.exists()) { throw new FileNotFoundException("File " + file + " does not exist."); } return result.build(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } LocalLocation other = (LocalLocation) o; return Objects.equal(file, other.file); } @Override public int hashCode() { return file.hashCode(); } }