001/*
002 * SonarQube
003 * Copyright (C) 2009-2017 SonarSource SA
004 * mailto:info AT sonarsource DOT com
005 *
006 * This program is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.api.utils;
021
022import com.google.common.base.Preconditions;
023import com.google.common.base.Throwables;
024import com.google.common.io.Files;
025import java.io.File;
026import java.io.IOException;
027import java.net.URI;
028import java.nio.charset.Charset;
029import java.util.Arrays;
030import java.util.HashMap;
031import java.util.Locale;
032import java.util.Map;
033import java.util.stream.Stream;
034import org.sonar.api.batch.ScannerSide;
035import org.sonar.api.ce.ComputeEngineSide;
036import org.sonar.api.server.ServerSide;
037
038/**
039 * Reads different types of URI. Supported schemes are http and file.
040 *
041 * @since 3.2
042 */
043@ScannerSide
044@ServerSide
045@ComputeEngineSide
046public class UriReader {
047
048  private final Map<String, SchemeProcessor> processorsByScheme = new HashMap<>();
049
050  public UriReader(SchemeProcessor[] processors) {
051    Stream.concat(Stream.of(new FileProcessor()), Arrays.stream(processors)).forEach(processor -> {
052      for (String scheme : processor.getSupportedSchemes()) {
053        processorsByScheme.put(scheme.toLowerCase(Locale.ENGLISH), processor);
054      }
055    });
056  }
057
058  /**
059   * Reads all bytes from uri. It throws an unchecked exception if an error occurs.
060   */
061  public byte[] readBytes(URI uri) {
062    return searchForSupportedProcessor(uri).readBytes(uri);
063  }
064
065  /**
066   * Reads all characters from uri, using the given character set.
067   * It throws an unchecked exception if an error occurs.
068   */
069  public String readString(URI uri, Charset charset) {
070    return searchForSupportedProcessor(uri).readString(uri, charset);
071  }
072
073  /**
074   * Returns a detailed description of the given uri. For example HTTP URIs are completed
075   * with the configured HTTP proxy.
076   */
077  public String description(URI uri) {
078    SchemeProcessor reader = searchForSupportedProcessor(uri);
079
080    return reader.description(uri);
081  }
082
083  SchemeProcessor searchForSupportedProcessor(URI uri) {
084    SchemeProcessor processor = processorsByScheme.get(uri.getScheme().toLowerCase(Locale.ENGLISH));
085    Preconditions.checkArgument(processor != null, "URI schema is not supported: " + uri.getScheme());
086    return processor;
087  }
088
089  public abstract static class SchemeProcessor {
090    protected abstract String[] getSupportedSchemes();
091
092    protected abstract byte[] readBytes(URI uri);
093
094    protected abstract String readString(URI uri, Charset charset);
095
096    protected abstract String description(URI uri);
097  }
098
099  /**
100   * This implementation is not exposed in API and is kept private.
101   */
102  private static class FileProcessor extends SchemeProcessor {
103
104    @Override
105    public String[] getSupportedSchemes() {
106      return new String[]{"file"};
107    }
108
109    @Override
110    protected byte[] readBytes(URI uri) {
111      try {
112        return Files.toByteArray(new File(uri));
113      } catch (IOException e) {
114        throw Throwables.propagate(e);
115      }
116    }
117
118    @Override
119    protected String readString(URI uri, Charset charset) {
120      try {
121        return Files.toString(new File(uri), charset);
122      } catch (IOException e) {
123        throw Throwables.propagate(e);
124      }
125    }
126
127    @Override
128    protected String description(URI uri) {
129      return new File(uri).getAbsolutePath();
130    }
131  }
132}