001/*
002 * Copyright (C) 2007 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.io;
018
019import static com.google.common.base.Preconditions.checkArgument;
020import static com.google.common.base.Preconditions.checkNotNull;
021
022import com.google.common.annotations.Beta;
023import com.google.common.base.Charsets;
024import com.google.common.base.Objects;
025import com.google.common.collect.Lists;
026
027import java.io.IOException;
028import java.io.InputStream;
029import java.io.InputStreamReader;
030import java.io.OutputStream;
031import java.net.URL;
032import java.nio.charset.Charset;
033import java.util.List;
034
035/**
036 * Provides utility methods for working with resources in the classpath.
037 * Note that even though these methods use {@link URL} parameters, they
038 * are usually not appropriate for HTTP or other non-classpath resources.
039 *
040 * <p>All method parameters must be non-null unless documented otherwise.
041 *
042 * @author Chris Nokleberg
043 * @author Ben Yu
044 * @author Colin Decker
045 * @since 1.0
046 */
047@Beta
048public final class Resources {
049  private Resources() {}
050
051  /**
052   * Returns a factory that will supply instances of {@link InputStream} that
053   * read from the given URL.
054   *
055   * @param url the URL to read from
056   * @return the factory
057   */
058  public static InputSupplier<InputStream> newInputStreamSupplier(URL url) {
059    return ByteStreams.asInputSupplier(asByteSource(url));
060  }
061
062  /**
063   * Returns a {@link ByteSource} that reads from the given URL.
064   *
065   * @since 14.0
066   */
067  public static ByteSource asByteSource(URL url) {
068    return new UrlByteSource(url);
069  }
070
071  /**
072   * A byte source that reads from a URL using {@link URL#openStream()}.
073   */
074  private static final class UrlByteSource extends ByteSource {
075
076    private final URL url;
077
078    private UrlByteSource(URL url) {
079      this.url = checkNotNull(url);
080    }
081
082    @Override
083    public InputStream openStream() throws IOException {
084      return url.openStream();
085    }
086
087    @Override
088    public String toString() {
089      return "Resources.asByteSource(" + url + ")";
090    }
091  }
092
093  /**
094   * Returns a factory that will supply instances of
095   * {@link InputStreamReader} that read a URL using the given character set.
096   *
097   * @param url the URL to read from
098   * @param charset the charset used to decode the input stream; see {@link
099   *     Charsets} for helpful predefined constants
100   * @return the factory
101   */
102  public static InputSupplier<InputStreamReader> newReaderSupplier(
103      URL url, Charset charset) {
104    return CharStreams.asInputSupplier(asCharSource(url, charset));
105  }
106
107  /**
108   * Returns a {@link CharSource} that reads from the given URL using the given character set.
109   *
110   * @since 14.0
111   */
112  public static CharSource asCharSource(URL url, Charset charset) {
113    return asByteSource(url).asCharSource(charset);
114  }
115
116  /**
117   * Reads all bytes from a URL into a byte array.
118   *
119   * @param url the URL to read from
120   * @return a byte array containing all the bytes from the URL
121   * @throws IOException if an I/O error occurs
122   */
123  public static byte[] toByteArray(URL url) throws IOException {
124    return asByteSource(url).read();
125  }
126
127  /**
128   * Reads all characters from a URL into a {@link String}, using the given
129   * character set.
130   *
131   * @param url the URL to read from
132   * @param charset the charset used to decode the input stream; see {@link
133   *     Charsets} for helpful predefined constants
134   * @return a string containing all the characters from the URL
135   * @throws IOException if an I/O error occurs.
136   */
137  public static String toString(URL url, Charset charset) throws IOException {
138    return asCharSource(url, charset).read();
139  }
140
141  /**
142   * Streams lines from a URL, stopping when our callback returns false, or we
143   * have read all of the lines.
144   *
145   * @param url the URL to read from
146   * @param charset the charset used to decode the input stream; see {@link
147   *     Charsets} for helpful predefined constants
148   * @param callback the LineProcessor to use to handle the lines
149   * @return the output of processing the lines
150   * @throws IOException if an I/O error occurs
151   */
152  public static <T> T readLines(URL url, Charset charset,
153      LineProcessor<T> callback) throws IOException {
154    return CharStreams.readLines(newReaderSupplier(url, charset), callback);
155  }
156
157  /**
158   * Reads all of the lines from a URL. The lines do not include
159   * line-termination characters, but do include other leading and trailing
160   * whitespace.
161   *
162   * <p>This method returns a mutable {@code List}. For an
163   * {@code ImmutableList}, use
164   * {@code Resources.asCharSource(url, charset).readLines()}.
165   *
166   * @param url the URL to read from
167   * @param charset the charset used to decode the input stream; see {@link
168   *     Charsets} for helpful predefined constants
169   * @return a mutable {@link List} containing all the lines
170   * @throws IOException if an I/O error occurs
171   */
172  public static List<String> readLines(URL url, Charset charset)
173      throws IOException {
174    // don't use asCharSource(url, charset).readLines() because that returns
175    // an immutable list, which would change the behavior of this method
176    return readLines(url, charset, new LineProcessor<List<String>>() {
177      final List<String> result = Lists.newArrayList();
178
179      @Override
180      public boolean processLine(String line) {
181        result.add(line);
182        return true;
183      }
184
185      @Override
186      public List<String> getResult() {
187        return result;
188      }
189    });
190  }
191
192  /**
193   * Copies all bytes from a URL to an output stream.
194   *
195   * @param from the URL to read from
196   * @param to the output stream
197   * @throws IOException if an I/O error occurs
198   */
199  public static void copy(URL from, OutputStream to) throws IOException {
200    asByteSource(from).copyTo(to);
201  }
202  
203  /**
204   * Returns a {@code URL} pointing to {@code resourceName} if the resource is
205   * found using the {@linkplain Thread#getContextClassLoader() context class
206   * loader}. In simple environments, the context class loader will find
207   * resources from the class path. In environments where different threads can
208   * have different class loaders, for example app servers, the context class
209   * loader will typically have been set to an appropriate loader for the
210   * current thread.
211   *
212   * <p>In the unusual case where the context class loader is null, the class
213   * loader that loaded this class ({@code Resources}) will be used instead.
214   * 
215   * @throws IllegalArgumentException if the resource is not found
216   */
217  public static URL getResource(String resourceName) {
218    ClassLoader loader = Objects.firstNonNull(
219        Thread.currentThread().getContextClassLoader(),
220        Resources.class.getClassLoader());
221    URL url = loader.getResource(resourceName);
222    checkArgument(url != null, "resource %s not found.", resourceName);
223    return url;
224  }
225
226  /**
227   * Given a {@code resourceName} that is relative to {@code contextClass},
228   * returns a {@code URL} pointing to the named resource.
229   * 
230   * @throws IllegalArgumentException if the resource is not found
231   */
232  public static URL getResource(Class<?> contextClass, String resourceName) {
233    URL url = contextClass.getResource(resourceName);
234    checkArgument(url != null, "resource %s relative to %s not found.",
235        resourceName, contextClass.getName());
236    return url;
237  }
238}