001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.vfs2; 018 019import java.util.Arrays; 020import java.util.Iterator; 021import java.util.Map; 022import java.util.SortedMap; 023import java.util.TreeMap; 024 025/** 026 * Configures file systems individually with these options. 027 * <p> 028 * To configure a file system, you set properties on a {@link FileSystemOptions} object. Most file systems provide a 029 * {@link FileSystemConfigBuilder} with specific options for that file system. 030 * <p> 031 * To use the options, pass them to {@link FileSystemManager#resolveFile(String,FileSystemOptions)}. From there, the 032 * options apply to all files that are resolved relative to that file. 033 * 034 * @see org.apache.commons.vfs2.impl.DefaultFileSystemConfigBuilder 035 * @see org.apache.commons.vfs2.provider.ftp.FtpFileSystemConfigBuilder 036 * @see org.apache.commons.vfs2.provider.ftps.FtpsFileSystemConfigBuilder 037 * @see org.apache.commons.vfs2.provider.hdfs.HdfsFileSystemConfigBuilder 038 * @see org.apache.commons.vfs2.provider.http.HttpFileSystemConfigBuilder 039 * @see org.apache.commons.vfs2.provider.webdav.WebdavFileSystemConfigBuilder 040 * @see org.apache.commons.vfs2.provider.ram.RamFileSystemConfigBuilder 041 * @see org.apache.commons.vfs2.provider.res.ResourceFileSystemConfigBuilder 042 * @see org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder 043 * 044 */ 045public final class FileSystemOptions implements Cloneable { 046 /** The options */ 047 private final Map<FileSystemOptionKey, Object> options; 048 049 /** 050 * Creates a new instance. 051 */ 052 public FileSystemOptions() { 053 this(new TreeMap<FileSystemOptionKey, Object>()); 054 } 055 056 protected FileSystemOptions(final Map<FileSystemOptionKey, Object> options) { 057 this.options = options; 058 } 059 060 /** 061 * Keys in the options Map. 062 */ 063 private static final class FileSystemOptionKey implements Comparable<FileSystemOptionKey> { 064 /** Constant used to create hashcode */ 065 private static final int HASH = 29; 066 067 /** The FileSystem class */ 068 private final Class<? extends FileSystem> fileSystemClass; 069 070 /** The option name */ 071 private final String name; 072 073 // TODO: the parameter name suggests that the class should only be a 074 // a FileSystem, however some of the tests pass in DefaultFileSystemConfigBuilder 075 private FileSystemOptionKey(final Class<? extends FileSystem> fileSystemClass, final String name) { 076 this.fileSystemClass = fileSystemClass; 077 this.name = name; 078 } 079 080 @Override 081 public int compareTo(final FileSystemOptionKey o) { 082 final int ret = fileSystemClass.getName().compareTo(o.fileSystemClass.getName()); 083 if (ret != 0) { 084 return ret; 085 } 086 return name.compareTo(o.name); 087 } 088 089 @Override 090 public boolean equals(final Object o) { 091 if (this == o) { 092 return true; 093 } 094 if (o == null || getClass() != o.getClass()) { 095 return false; 096 } 097 098 final FileSystemOptionKey that = (FileSystemOptionKey) o; 099 100 if (!fileSystemClass.equals(that.fileSystemClass)) { 101 return false; 102 } 103 if (!name.equals(that.name)) { 104 return false; 105 } 106 107 return true; 108 } 109 110 @Override 111 public int hashCode() { 112 int result; 113 result = fileSystemClass.hashCode(); 114 result = HASH * result + name.hashCode(); 115 return result; 116 } 117 118 @Override 119 public String toString() { 120 return fileSystemClass.getName() + "." + name; 121 } 122 } 123 124 void setOption(final Class<? extends FileSystem> fileSystemClass, final String name, final Object value) { 125 options.put(new FileSystemOptionKey(fileSystemClass, name), value); 126 } 127 128 Object getOption(final Class<? extends FileSystem> fileSystemClass, final String name) { 129 final FileSystemOptionKey key = new FileSystemOptionKey(fileSystemClass, name); 130 return options.get(key); 131 } 132 133 boolean hasOption(final Class<? extends FileSystem> fileSystemClass, final String name) { 134 final FileSystemOptionKey key = new FileSystemOptionKey(fileSystemClass, name); 135 return options.containsKey(key); 136 } 137 138 public int compareTo(final FileSystemOptions other) { 139 if (this == other) { 140 // the same instance 141 return 0; 142 } 143 144 final int propsSz = options == null ? 0 : options.size(); 145 final int propsFkSz = other.options == null ? 0 : other.options.size(); 146 if (propsSz < propsFkSz) { 147 return -1; 148 } 149 if (propsSz > propsFkSz) { 150 return 1; 151 } 152 if (propsSz == 0) { 153 // props empty 154 return 0; 155 } 156 157 // ensure proper sequence of options 158 final SortedMap<FileSystemOptionKey, Object> myOptions = options instanceof SortedMap 159 ? (SortedMap<FileSystemOptionKey, Object>) options 160 : new TreeMap<>(options); 161 final SortedMap<FileSystemOptionKey, Object> theirOptions = other.options instanceof SortedMap 162 ? (SortedMap<FileSystemOptionKey, Object>) other.options 163 : new TreeMap<>(other.options); 164 final Iterator<FileSystemOptionKey> optKeysIter = myOptions.keySet().iterator(); 165 final Iterator<FileSystemOptionKey> otherKeysIter = theirOptions.keySet().iterator(); 166 while (optKeysIter.hasNext()) { 167 final int comp = optKeysIter.next().compareTo(otherKeysIter.next()); 168 if (comp != 0) { 169 return comp; 170 } 171 } 172 173 final Object[] array = new Object[propsSz]; 174 final int hash = Arrays.deepHashCode(myOptions.values().toArray(array)); 175 final int hashFk = Arrays.deepHashCode(theirOptions.values().toArray(array)); 176 if (hash < hashFk) { 177 return -1; 178 } 179 if (hash > hashFk) { 180 return 1; 181 } 182 183 // TODO: compare Entry by Entry ?? 184 return 0; 185 } 186 187 @Override 188 public int hashCode() { 189 final int prime = 31; 190 int result = 1; 191 if (options == null) { 192 result = prime * result; 193 } else { 194 final SortedMap<FileSystemOptionKey, Object> myOptions = options instanceof SortedMap 195 ? (SortedMap<FileSystemOptionKey, Object>) options 196 : new TreeMap<>(options); 197 result = prime * result + myOptions.keySet().hashCode(); 198 result = prime * result + Arrays.deepHashCode(myOptions.values().toArray(new Object[options.size()])); 199 } 200 return result; 201 } 202 203 @Override 204 public boolean equals(final Object obj) { 205 if (this == obj) { 206 return true; 207 } 208 if (obj == null) { 209 return false; 210 } 211 if (getClass() != obj.getClass()) { 212 return false; 213 } 214 final FileSystemOptions other = (FileSystemOptions) obj; 215 return compareTo(other) == 0; 216 } 217 218 /** 219 * {@inheritDoc} 220 * 221 * @since 2.0 222 */ 223 @Override 224 public Object clone() { 225 return new FileSystemOptions(new TreeMap<>(options)); 226 } 227 228 @Override 229 public String toString() { 230 return options.toString(); 231 } 232}