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.annotations.VisibleForTesting;
023import com.google.common.base.Preconditions;
024import com.google.common.base.Throwables;
025import com.google.common.collect.Lists;
026import com.google.common.collect.Maps;
027import com.google.common.io.Files;
028import java.io.File;
029import java.io.IOException;
030import java.net.URI;
031import java.nio.charset.Charset;
032import java.util.List;
033import java.util.Locale;
034import java.util.Map;
035import org.sonar.api.batch.ScannerSide;
036import org.sonar.api.ce.ComputeEngineSide;
037import org.sonar.api.server.ServerSide;
038
039/**
040 * Reads different types of URI. Supported schemes are http and file.
041 *
042 * @since 3.2
043 */
044@ScannerSide
045@ServerSide
046@ComputeEngineSide
047public class UriReader {
048
049  private final Map<String, SchemeProcessor> processorsByScheme = Maps.newHashMap();
050
051  public UriReader(SchemeProcessor[] processors) {
052    List<SchemeProcessor> allProcessors = Lists.asList(new FileProcessor(), processors);
053    for (SchemeProcessor processor : allProcessors) {
054      for (String scheme : processor.getSupportedSchemes()) {
055        processorsByScheme.put(scheme.toLowerCase(Locale.ENGLISH), processor);
056      }
057    }
058  }
059
060  /**
061   * Reads all bytes from uri. It throws an unchecked exception if an error occurs.
062   */
063  public byte[] readBytes(URI uri) {
064    return searchForSupportedProcessor(uri).readBytes(uri);
065  }
066
067  /**
068   * Reads all characters from uri, using the given character set.
069   * It throws an unchecked exception if an error occurs.
070   */
071  public String readString(URI uri, Charset charset) {
072    return searchForSupportedProcessor(uri).readString(uri, charset);
073  }
074
075  /**
076   * Returns a detailed description of the given uri. For example HTTP URIs are completed
077   * with the configured HTTP proxy.
078   */
079  public String description(URI uri) {
080    SchemeProcessor reader = searchForSupportedProcessor(uri);
081
082    return reader.description(uri);
083  }
084
085  @VisibleForTesting
086  SchemeProcessor searchForSupportedProcessor(URI uri) {
087    SchemeProcessor processor = processorsByScheme.get(uri.getScheme().toLowerCase(Locale.ENGLISH));
088    Preconditions.checkArgument(processor != null, "URI schema is not supported: " + uri.getScheme());
089    return processor;
090  }
091
092  public abstract static class SchemeProcessor {
093    protected abstract String[] getSupportedSchemes();
094
095    protected abstract byte[] readBytes(URI uri);
096
097    protected abstract String readString(URI uri, Charset charset);
098
099    protected abstract String description(URI uri);
100  }
101
102  /**
103   * This implementation is not exposed in API and is kept private.
104   */
105  private static class FileProcessor extends SchemeProcessor {
106
107    @Override
108    public String[] getSupportedSchemes() {
109      return new String[] {"file"};
110    }
111
112    @Override
113    protected byte[] readBytes(URI uri) {
114      try {
115        return Files.toByteArray(new File(uri));
116      } catch (IOException e) {
117        throw Throwables.propagate(e);
118      }
119    }
120
121    @Override
122    protected String readString(URI uri, Charset charset) {
123      try {
124        return Files.toString(new File(uri), charset);
125      } catch (IOException e) {
126        throw Throwables.propagate(e);
127      }
128    }
129
130    @Override
131    protected String description(URI uri) {
132      return new File(uri).getAbsolutePath();
133    }
134  }
135}