Пример #1
0
  static private List getCommandCompilerCPP(String avrBasePath,
    List includePaths, String sourceName, String objectName,
    Map<String, String> boardPreferences) {
    
    List baseCommandCompilerCPP = new ArrayList(Arrays.asList(new String[] {
      avrBasePath + "avr-g++",
      "-c", // compile, don't link
      "-g", // include debugging info (so errors include line numbers)
      "-Os", // optimize for size
      Preferences.getBoolean("build.verbose") ? "-Wall" : "-w", // show warnings if verbose
      "-fno-exceptions",
      "-ffunction-sections", // place each function in its own section
      "-fdata-sections",
      "-mmcu=" + boardPreferences.get("build.mcu"),
      "-DF_CPU=" + boardPreferences.get("build.f_cpu"),
      "-MMD", // output dependancy info
      "-DUSB_VID=" + boardPreferences.get("build.vid"),
      "-DUSB_PID=" + boardPreferences.get("build.pid"),      
      "-DARDUINO=" + Base.REVISION,
    }));

    for (int i = 0; i < includePaths.size(); i++) {
      baseCommandCompilerCPP.add("-I" + (String) includePaths.get(i));
    }

    baseCommandCompilerCPP.add(sourceName);
    baseCommandCompilerCPP.add("-o");
    baseCommandCompilerCPP.add(objectName);

    return baseCommandCompilerCPP;
  }
Пример #2
0
 public Iterable<ZipEntry> getEntriesInPhysicalOrder(final String name) {
   ZipEntry[] entriesOfThatName = new ZipEntry[0];
   if (this.nameMap.containsKey(name)) {
     entriesOfThatName = this.nameMap.get(name).toArray(entriesOfThatName);
     Arrays.sort(entriesOfThatName, this.OFFSET_COMPARATOR);
   }
   return Arrays.asList(entriesOfThatName);
 }
Пример #3
0
    public void actionPerformed(ActionEvent e) {
      if (!this.isEnabled()) {
        return;
      }

      if (NEW_AIRSPACE.equals(e.getActionCommand())) {
        this.createNewEntry(this.getView().getSelectedFactory());
      } else if (CLEAR_SELECTION.equals(e.getActionCommand())) {
        this.selectEntry(null, true);
      } else if (SIZE_NEW_SHAPES_TO_VIEWPORT.equals(e.getActionCommand())) {
        if (e.getSource() instanceof AbstractButton) {
          boolean selected = ((AbstractButton) e.getSource()).isSelected();
          this.setResizeNewShapesToViewport(selected);
        }
      } else if (ENABLE_EDIT.equals(e.getActionCommand())) {
        if (e.getSource() instanceof AbstractButton) {
          boolean selected = ((AbstractButton) e.getSource()).isSelected();
          this.setEnableEdit(selected);
        }
      } else if (OPEN.equals(e.getActionCommand())) {
        this.openFromFile();
      } else if (OPEN_URL.equals(e.getActionCommand())) {
        this.openFromURL();
      } else if (OPEN_DEMO_AIRSPACES.equals(e.getActionCommand())) {
        this.openFromPath(DEMO_AIRSPACES_PATH);
        this.zoomTo(
            LatLon.fromDegrees(47.6584074779224, -122.3059199579634),
            Angle.fromDegrees(-152),
            Angle.fromDegrees(75),
            750);
      } else if (REMOVE_SELECTED.equals(e.getActionCommand())) {
        this.removeEntries(Arrays.asList(this.getSelectedEntries()));
      } else if (SAVE.equals(e.getActionCommand())) {
        this.saveToFile();
      } else if (SELECTION_CHANGED.equals(e.getActionCommand())) {
        this.viewSelectionChanged();
      }
    }
Пример #4
0
  static private List getCommandCompilerS(String avrBasePath, List includePaths,
    String sourceName, String objectName, Map<String, String> boardPreferences) {
    List baseCommandCompiler = new ArrayList(Arrays.asList(new String[] {
      avrBasePath + "avr-gcc",
      "-c", // compile, don't link
      "-g", // include debugging info (so errors include line numbers)
      "-assembler-with-cpp",
      "-mmcu=" + boardPreferences.get("build.mcu"),
      "-DF_CPU=" + boardPreferences.get("build.f_cpu"),      
      "-DARDUINO=" + Base.REVISION,
      "-DUSB_VID=" + boardPreferences.get("build.vid"),
      "-DUSB_PID=" + boardPreferences.get("build.pid"),
    }));

    for (int i = 0; i < includePaths.size(); i++) {
      baseCommandCompiler.add("-I" + (String) includePaths.get(i));
    }

    baseCommandCompiler.add(sourceName);
    baseCommandCompiler.add("-o"+ objectName);

    return baseCommandCompiler;
  }
Пример #5
0
  /**
   * Run command in separated console.
   *
   * @param workFolder Work folder for command.
   * @param args A string array containing the program and its arguments.
   * @return Started process.
   * @throws IOException If failed to start process.
   */
  public static Process openInConsole(@Nullable File workFolder, String... args)
      throws IOException {
    String[] commands = args;

    String cmd = F.concat(Arrays.asList(args), " ");

    if (U.isWindows()) commands = F.asArray("cmd", "/c", String.format("start %s", cmd));

    if (U.isMacOs())
      commands =
          F.asArray(
              "osascript",
              "-e",
              String.format("tell application \"Terminal\" to do script \"%s\"", cmd));

    if (U.isUnix()) commands = F.asArray("xterm", "-sl", "1024", "-geometry", "200x50", "-e", cmd);

    ProcessBuilder pb = new ProcessBuilder(commands);

    if (workFolder != null) pb.directory(workFolder);

    return pb.start();
  }
Пример #6
0
  /**
   * Compile with avr-gcc.
   *
   * @param sketch Sketch object to be compiled.
   * @param buildPath Where the temporary files live and will be built from.
   * @param primaryClassName the name of the combined sketch file w/ extension
   * @return true if successful.
   * @throws RunnerException Only if there's a problem. Only then.
   * 
  * [ROBOTIS]Changed prototype to support ARM Cortex-M3 based CM-900 Pandora project
  * 2012-09-26 [email protected]
  * */
  public boolean compile(Sketch sketch, //change return type[ROBOTIS]
                         String buildPath,
                         String primaryClassName,
                         boolean verbose,
                         List<String> ignored) throws RunnerException {
    this.sketch = sketch;
    this.buildPath = buildPath;
    this.primaryClassName = primaryClassName; //예를 들면 cpp파일로 변환된 AnalogReadSerial.cpp
    this.verbose = verbose;
    this.sketchIsCompiled = false;
    System.out.println("Compiler.compile() sketch ="+sketch.getName()+"buildpath ="+buildPath+"primaryClassName ="+primaryClassName);
    // the pms object isn't used for anything but storage
    MessageStream pms = new MessageStream(this);

    String avrBasePath = Base.getAvrBasePath();
    System.out.println("[ROBOTIS]avrBasePath ="+avrBasePath);
    Map<String, String> boardPreferences = Base.getBoardPreferences();
    String core = boardPreferences.get("build.core");
    System.out.println("[ROBOTIS]build.core ="+core);
    if (core == null) {
    	RunnerException re = new RunnerException(_("No board selected; please choose a board from the Tools > Board menu."));
      re.hideStackTrace();
      throw re;
    }
    String corePath;
    
    if (core.indexOf(':') == -1) {
      Target t = Base.getTarget();
      File coreFolder = new File(new File(t.getFolder(), "cores"), core);
      corePath = coreFolder.getAbsolutePath();
      
    } else {
      Target t = Base.targetsTable.get(core.substring(0, core.indexOf(':')));
      File coreFolder = new File(t.getFolder(), "cores");
      coreFolder = new File(coreFolder, core.substring(core.indexOf(':') + 1));
      corePath = coreFolder.getAbsolutePath();
    }

    System.out.println("[ROBOTIS]corePath ="+corePath);
    
    String variant = boardPreferences.get("build.variant");
    String variantPath = null;
    
    if (variant != null) {
      if (variant.indexOf(':') == -1) {
	Target t = Base.getTarget();
	File variantFolder = new File(new File(t.getFolder(), "variants"), variant);
	variantPath = variantFolder.getAbsolutePath();
      } else {
	Target t = Base.targetsTable.get(variant.substring(0, variant.indexOf(':')));
	File variantFolder = new File(t.getFolder(), "variants");
	variantFolder = new File(variantFolder, variant.substring(variant.indexOf(':') + 1));
	variantPath = variantFolder.getAbsolutePath();
      }
    }

    List<File> objectFiles = new ArrayList<File>();

   // 0. include paths for core + all libraries

   sketch.setCompilingProgress(20);
   List includePaths = new ArrayList();
   includePaths.add(corePath);
   if (variantPath != null) includePaths.add(variantPath);
   for (File file : sketch.getImportedLibraries()) {
     includePaths.add(file.getPath());
   }

   // 1. compile the sketch (already in the buildPath)

   sketch.setCompilingProgress(30);
   objectFiles.addAll(
     compileFiles(avrBasePath, buildPath, includePaths,
               findFilesInPath(buildPath, "S", false),
               findFilesInPath(buildPath, "c", false),
               findFilesInPath(buildPath, "cpp", false),
               boardPreferences));
   sketchIsCompiled = true;

   // 2. compile the libraries, outputting .o files to: <buildPath>/<library>/

   sketch.setCompilingProgress(40);
   for (File libraryFolder : sketch.getImportedLibraries()) {
     File outputFolder = new File(buildPath, libraryFolder.getName());
     File utilityFolder = new File(libraryFolder, "utility");
     createFolder(outputFolder);
     // this library can use includes in its utility/ folder
     includePaths.add(utilityFolder.getAbsolutePath());
     objectFiles.addAll(
       compileFiles(avrBasePath, outputFolder.getAbsolutePath(), includePaths,
               findFilesInFolder(libraryFolder, "S", false),
               findFilesInFolder(libraryFolder, "c", false),
               findFilesInFolder(libraryFolder, "cpp", false),
               boardPreferences));
     outputFolder = new File(outputFolder, "utility");
     createFolder(outputFolder);
     objectFiles.addAll(
       compileFiles(avrBasePath, outputFolder.getAbsolutePath(), includePaths,
               findFilesInFolder(utilityFolder, "S", false),
               findFilesInFolder(utilityFolder, "c", false),
               findFilesInFolder(utilityFolder, "cpp", false),
               boardPreferences));
     // other libraries should not see this library's utility/ folder
     includePaths.remove(includePaths.size() - 1);
   }

   // 3. compile the core, outputting .o files to <buildPath> and then
   // collecting them into the core.a library file.

   sketch.setCompilingProgress(50);
  includePaths.clear();
  includePaths.add(corePath);  // include path for core only
  if (variantPath != null) includePaths.add(variantPath);
  List<File> coreObjectFiles =
    compileFiles(avrBasePath, buildPath, includePaths,
              findFilesInPath(corePath, "S", true),
              findFilesInPath(corePath, "c", true),
              findFilesInPath(corePath, "cpp", true),
              boardPreferences);

   String runtimeLibraryName = buildPath + File.separator + "core.a";
   List baseCommandAR = new ArrayList(Arrays.asList(new String[] {
     avrBasePath + "avr-ar",
     "rcs",
     runtimeLibraryName
   }));
   for(File file : coreObjectFiles) {
     List commandAR = new ArrayList(baseCommandAR);
     commandAR.add(file.getAbsolutePath());
     execAsynchronously(commandAR);
   }

    // 4. link it all together into the .elf file
    // For atmega2560, need --relax linker option to link larger
    // programs correctly.
    String optRelax = "";
    String atmega2560 = new String ("atmega2560");
    if ( atmega2560.equals(boardPreferences.get("build.mcu")) ) {
        optRelax = new String(",--relax");
    }
   sketch.setCompilingProgress(60);
    List baseCommandLinker = new ArrayList(Arrays.asList(new String[] {
      avrBasePath + "avr-gcc",
      "-Os",
      "-Wl,--gc-sections"+optRelax,
      "-mmcu=" + boardPreferences.get("build.mcu"),
      "-o",
      buildPath + File.separator + primaryClassName + ".elf"
    }));

    for (File file : objectFiles) {
      baseCommandLinker.add(file.getAbsolutePath());
    }

    baseCommandLinker.add(runtimeLibraryName);
    baseCommandLinker.add("-L" + buildPath);
    baseCommandLinker.add("-lm");

    execAsynchronously(baseCommandLinker);

    List baseCommandObjcopy = new ArrayList(Arrays.asList(new String[] {
      avrBasePath + "avr-objcopy",
      "-O",
      "-R",
    }));
    
    List commandObjcopy;

    // 5. extract EEPROM data (from EEMEM directive) to .eep file.
    sketch.setCompilingProgress(70);
    commandObjcopy = new ArrayList(baseCommandObjcopy);
    commandObjcopy.add(2, "ihex");
    commandObjcopy.set(3, "-j");
    commandObjcopy.add(".eeprom");
    commandObjcopy.add("--set-section-flags=.eeprom=alloc,load");
    commandObjcopy.add("--no-change-warnings");
    commandObjcopy.add("--change-section-lma");
    commandObjcopy.add(".eeprom=0");
    commandObjcopy.add(buildPath + File.separator + primaryClassName + ".elf");
    commandObjcopy.add(buildPath + File.separator + primaryClassName + ".eep");
    execAsynchronously(commandObjcopy);
    
    // 6. build the .hex file
    sketch.setCompilingProgress(80);
    commandObjcopy = new ArrayList(baseCommandObjcopy);
    commandObjcopy.add(2, "ihex");
    commandObjcopy.add(".eeprom"); // remove eeprom data
    commandObjcopy.add(buildPath + File.separator + primaryClassName + ".elf");
    commandObjcopy.add(buildPath + File.separator + primaryClassName + ".hex");
    execAsynchronously(commandObjcopy);
    
    sketch.setCompilingProgress(90);
   
    return true;
  }
Пример #7
0
 public Enumeration<ZipEntry> getEntriesInPhysicalOrder() {
   final ZipEntry[] allEntries = this.entries.toArray(new ZipEntry[0]);
   Arrays.sort(allEntries, this.OFFSET_COMPARATOR);
   return Collections.enumeration(Arrays.asList(allEntries));
 }
Пример #8
0
 public static String join(char sep, Object[] array) {
   return join(sep, Arrays.asList(array));
 }
/**
 * Implementations of this abstract class can add compression of a particular type to a given {@link
 * OutputStream}. They each return a {@link CompressingOutputStream}, which is just a thin wrapper
 * on top of an {@link OutputStream} that adds the ability to "finish" a stream (see {@link
 * CompressingOutputStream}).
 *
 * <p>
 *
 * <p>This class contains implementations based on several popular compression algorithms, such as
 * gzip. For example, the gzip implementation can decorate an {@link OutputStream} using an instance
 * of {@link GZIPOutputStream} and in that way add gzip compression to the stream.
 *
 * @author Sean Owen
 */
abstract class CompressingStreamFactory {
  /** "No encoding" content type: "identity". */
  static final String NO_ENCODING = "identity";

  private static final Logger LOG = LoggerFactory.getLogger(CompressingStreamFactory.class);
  /** Implementation based on {@link GZIPOutputStream} and {@link GZIPInputStream}. */
  private static final CompressingStreamFactory GZIP_CSF = new GZIPCompressingStreamFactory();
  /** Implementation based on {@link ZipOutputStream} and {@link ZipInputStream}. */
  private static final CompressingStreamFactory ZIP_CSF = new ZipCompressingStreamFactory();
  /** Implementation based on {@link DeflaterOutputStream}. */
  private static final CompressingStreamFactory DEFLATE_CSF = new DeflateCompressingStreamFactory();

  private static final String GZIP_ENCODING = "gzip";
  private static final String X_GZIP_ENCODING = "x-gzip";
  private static final String DEFLATE_ENCODING = "deflate";
  private static final String COMPRESS_ENCODING = "compress";
  private static final String X_COMPRESS_ENCODING = "x-compress";
  static final List<String> ALL_COMPRESSION_ENCODINGS =
      Collections.unmodifiableList(
          Arrays.asList(
              GZIP_ENCODING,
              DEFLATE_ENCODING,
              COMPRESS_ENCODING,
              X_GZIP_ENCODING,
              X_COMPRESS_ENCODING));

  /** "Any encoding" content type: the "*" wildcard. */
  private static final String ANY_ENCODING = "*";

  /** Ordered list of preferred encodings, from most to least preferred */
  private static final List<String> SUPPORTED_ENCODINGS;
  /**
   * Cache mapping previously seen "Accept-Encoding" header Strings to an appropriate instance of
   * {@link CompressingStreamFactory}.
   */
  private static final Map<String, String> BEST_ENCODING_CACHE =
      Collections.synchronizedMap(new HashMap<String, String>(101));
  /** Maps content type String to appropriate implementation of {@link CompressingStreamFactory}. */
  private static final Map<String, CompressingStreamFactory> FACTORY_MAP;

  private static final Pattern COMMA = Pattern.compile(",");

  static {
    List<String> temp = new ArrayList<>(6);
    temp.add(GZIP_ENCODING);
    temp.add(DEFLATE_ENCODING);
    temp.add(COMPRESS_ENCODING);
    temp.add(X_GZIP_ENCODING);
    temp.add(X_COMPRESS_ENCODING);
    temp.add(NO_ENCODING);
    SUPPORTED_ENCODINGS = Collections.unmodifiableList(temp);
  }

  static {
    Map<String, CompressingStreamFactory> temp = new HashMap<>(11);
    temp.put(GZIP_ENCODING, GZIP_CSF);
    temp.put(X_GZIP_ENCODING, GZIP_CSF);
    temp.put(COMPRESS_ENCODING, ZIP_CSF);
    temp.put(X_COMPRESS_ENCODING, ZIP_CSF);
    temp.put(DEFLATE_ENCODING, DEFLATE_CSF);
    FACTORY_MAP = Collections.unmodifiableMap(temp);
  }

  private static OutputStream maybeWrapStatsOutputStream(
      OutputStream outputStream,
      CompressingFilterContext context,
      CompressingFilterStats.StatsField field) {
    assert outputStream != null;
    OutputStream result;
    if (context.isStatsEnabled()) {
      CompressingFilterStats stats = context.getStats();
      CompressingFilterStats.OutputStatsCallback callbackOutput =
          stats.getOutputStatsCallback(field);
      result = new StatsOutputStream(outputStream, callbackOutput);
    } else {
      result = outputStream;
    }
    return result;
  }

  private static InputStream maybeWrapStatsInputStream(
      InputStream inputStream,
      CompressingFilterContext context,
      CompressingFilterStats.StatsField field) {
    assert inputStream != null;
    InputStream result;
    if (context.isStatsEnabled()) {
      CompressingFilterStats stats = context.getStats();
      CompressingFilterStats.InputStatsCallback callbackInput = stats.getInputStatsCallback(field);
      result = new StatsInputStream(inputStream, callbackInput);
    } else {
      result = inputStream;
    }
    return result;
  }

  private static boolean isSupportedResponseContentEncoding(String contentEncoding) {
    return NO_ENCODING.equals(contentEncoding) || FACTORY_MAP.containsKey(contentEncoding);
  }

  static boolean isSupportedRequestContentEncoding(String contentEncoding) {
    return NO_ENCODING.equals(contentEncoding) || FACTORY_MAP.containsKey(contentEncoding);
  }

  /**
   * Returns the instance associated to the given content encoding.
   *
   * @param contentEncoding content encoding (e.g. "gzip")
   * @return instance for content encoding
   */
  static CompressingStreamFactory getFactoryForContentEncoding(String contentEncoding) {
    assert FACTORY_MAP.containsKey(contentEncoding);
    return FACTORY_MAP.get(contentEncoding);
  }

  /**
   * Determines best content encoding for the response, based on the request -- in particular, based
   * on its "Accept-Encoding" header.
   *
   * @param httpRequest request
   * @return best content encoding
   */
  static String getBestContentEncoding(HttpServletRequest httpRequest) {

    String forcedEncoding = (String) httpRequest.getAttribute(CompressingFilter.FORCE_ENCODING_KEY);
    String bestEncoding;
    if (forcedEncoding != null) {

      bestEncoding = forcedEncoding;

    } else {

      String acceptEncodingHeader =
          httpRequest.getHeader(CompressingHttpServletResponse.ACCEPT_ENCODING_HEADER);
      if (acceptEncodingHeader == null) {

        bestEncoding = NO_ENCODING;

      } else {

        bestEncoding = BEST_ENCODING_CACHE.get(acceptEncodingHeader);

        if (bestEncoding == null) {

          // No cached value; must parse header to determine best encoding
          // I don't synchronize on bestEncodingCache; it's not worth it to avoid the rare case
          // where
          // two thread get in here and both parse the header. It's only a tiny bit of extra work,
          // and
          // avoids the synchronization overhead.

          if (acceptEncodingHeader.indexOf((int) ',') >= 0) {
            // multiple encodings are accepted
            bestEncoding = selectBestEncoding(acceptEncodingHeader);
          } else {
            // one encoding is accepted
            bestEncoding = parseBestEncoding(acceptEncodingHeader);
          }

          BEST_ENCODING_CACHE.put(acceptEncodingHeader, bestEncoding);
        }
      }
    }

    // User-specified encoding might not be supported
    if (!isSupportedResponseContentEncoding(bestEncoding)) {
      bestEncoding = NO_ENCODING;
    }

    return bestEncoding;
  }

  private static String parseBestEncoding(String acceptEncodingHeader) {
    ContentEncodingQ contentEncodingQ = parseContentEncodingQ(acceptEncodingHeader);
    String contentEncoding = contentEncodingQ.getContentEncoding();
    if (contentEncodingQ.getQ() > 0.0) {
      if (ANY_ENCODING.equals(contentEncoding)) {
        return SUPPORTED_ENCODINGS.get(0);
      } else if (SUPPORTED_ENCODINGS.contains(contentEncoding)) {
        return contentEncoding;
      }
    }
    return NO_ENCODING;
  }

  @SuppressWarnings("squid:S1244")
  private static String selectBestEncoding(String acceptEncodingHeader) {
    // multiple encodings are accepted; determine best one

    Collection<String> bestEncodings = new HashSet<>(3);
    double bestQ = 0.0;
    Collection<String> unacceptableEncodings = new HashSet<>(3);
    boolean willAcceptAnything = false;

    for (String token : COMMA.split(acceptEncodingHeader)) {
      ContentEncodingQ contentEncodingQ = parseContentEncodingQ(token);
      String contentEncoding = contentEncodingQ.getContentEncoding();
      double q = contentEncodingQ.getQ();
      if (ANY_ENCODING.equals(contentEncoding)) {
        willAcceptAnything = q > 0.0;
      } else if (SUPPORTED_ENCODINGS.contains(contentEncoding)) {
        if (q > 0.0) {
          // This is a header quality comparison.
          // So it is safe to suppress warning squid:S1244
          if (q == bestQ) {
            bestEncodings.add(contentEncoding);
          } else if (q > bestQ) {
            bestQ = q;
            bestEncodings.clear();
            bestEncodings.add(contentEncoding);
          }
        } else {
          unacceptableEncodings.add(contentEncoding);
        }
      }
    }

    if (bestEncodings.isEmpty()) {
      // nothing was acceptable to us
      if (willAcceptAnything) {
        if (unacceptableEncodings.isEmpty()) {
          return SUPPORTED_ENCODINGS.get(0);
        } else {
          for (String encoding : SUPPORTED_ENCODINGS) {
            if (!unacceptableEncodings.contains(encoding)) {
              return encoding;
            }
          }
        }
      }
    } else {
      for (String encoding : SUPPORTED_ENCODINGS) {
        if (bestEncodings.contains(encoding)) {
          return encoding;
        }
      }
    }

    return NO_ENCODING;
  }

  private static ContentEncodingQ parseContentEncodingQ(String contentEncodingString) {

    double q = 1.0;

    int qvalueStartIndex = contentEncodingString.indexOf((int) ';');
    String contentEncoding;
    if (qvalueStartIndex >= 0) {
      contentEncoding = contentEncodingString.substring(0, qvalueStartIndex).trim();
      String qvalueString = contentEncodingString.substring(qvalueStartIndex + 1).trim();
      if (qvalueString.startsWith("q=")) {
        try {
          q = Double.parseDouble(qvalueString.substring(2));
        } catch (NumberFormatException ignored) {
          // That's bad -- browser sent an invalid number. All we can do is ignore it, and
          // pretend that no q value was specified, so that it effectively defaults to 1.0
          LOG.trace("Couldn't parse a Double from {}.", qvalueString.substring(2), ignored);
        }
      }
    } else {
      contentEncoding = contentEncodingString.trim();
    }

    return new ContentEncodingQ(contentEncoding, q);
  }

  abstract CompressingOutputStream getCompressingStream(
      OutputStream servletOutputStream, CompressingFilterContext context) throws IOException;

  abstract CompressingInputStream getCompressingStream(
      InputStream servletInputStream, CompressingFilterContext context) throws IOException;

  private static final class ContentEncodingQ {

    private final String contentEncoding;
    private final double q;

    private ContentEncodingQ(String contentEncoding, double q) {
      assert contentEncoding != null && contentEncoding.length() > 0;
      this.contentEncoding = contentEncoding;
      this.q = q;
    }

    String getContentEncoding() {
      return contentEncoding;
    }

    double getQ() {
      return q;
    }

    @Override
    public String toString() {
      return contentEncoding + ";q=" + q;
    }
  }

  private static class GZIPCompressingStreamFactory extends CompressingStreamFactory {
    @Override
    CompressingOutputStream getCompressingStream(
        final OutputStream outputStream, final CompressingFilterContext context)
        throws IOException {
      return new CompressingOutputStream() {
        private final DeflaterOutputStream gzipOutputStream =
            new GZIPOutputStream(
                CompressingStreamFactory.maybeWrapStatsOutputStream(
                    outputStream,
                    context,
                    CompressingFilterStats.StatsField.RESPONSE_COMPRESSED_BYTES));
        private final OutputStream statsOutputStream =
            CompressingStreamFactory.maybeWrapStatsOutputStream(
                gzipOutputStream, context, CompressingFilterStats.StatsField.RESPONSE_INPUT_BYTES);

        @Override
        public OutputStream getCompressingOutputStream() {
          return statsOutputStream;
        }

        @Override
        public void finish() throws IOException {
          gzipOutputStream.finish();
        }
      };
    }

    @Override
    CompressingInputStream getCompressingStream(
        final InputStream inputStream, final CompressingFilterContext context) {
      return new CompressingInputStream() {
        @Override
        public InputStream getCompressingInputStream() throws IOException {
          return CompressingStreamFactory.maybeWrapStatsInputStream(
              new GZIPInputStream(
                  CompressingStreamFactory.maybeWrapStatsInputStream(
                      inputStream,
                      context,
                      CompressingFilterStats.StatsField.REQUEST_COMPRESSED_BYTES)),
              context,
              CompressingFilterStats.StatsField.REQUEST_INPUT_BYTES);
        }
      };
    }
  }

  private static class ZipCompressingStreamFactory extends CompressingStreamFactory {
    @Override
    CompressingOutputStream getCompressingStream(
        final OutputStream outputStream, final CompressingFilterContext context) {
      return new CompressingOutputStream() {
        private final DeflaterOutputStream zipOutputStream =
            new ZipOutputStream(
                CompressingStreamFactory.maybeWrapStatsOutputStream(
                    outputStream,
                    context,
                    CompressingFilterStats.StatsField.RESPONSE_COMPRESSED_BYTES));
        private final OutputStream statsOutputStream =
            CompressingStreamFactory.maybeWrapStatsOutputStream(
                zipOutputStream, context, CompressingFilterStats.StatsField.RESPONSE_INPUT_BYTES);

        @Override
        public OutputStream getCompressingOutputStream() {
          return statsOutputStream;
        }

        @Override
        public void finish() throws IOException {
          zipOutputStream.finish();
        }
      };
    }

    @Override
    CompressingInputStream getCompressingStream(
        final InputStream inputStream, final CompressingFilterContext context) {
      return new CompressingInputStream() {
        @Override
        public InputStream getCompressingInputStream() {
          return CompressingStreamFactory.maybeWrapStatsInputStream(
              new ZipInputStream(
                  CompressingStreamFactory.maybeWrapStatsInputStream(
                      inputStream,
                      context,
                      CompressingFilterStats.StatsField.REQUEST_COMPRESSED_BYTES)),
              context,
              CompressingFilterStats.StatsField.REQUEST_INPUT_BYTES);
        }
      };
    }
  }

  private static class DeflateCompressingStreamFactory extends CompressingStreamFactory {
    @Override
    CompressingOutputStream getCompressingStream(
        final OutputStream outputStream, final CompressingFilterContext context) {
      return new CompressingOutputStream() {
        private final DeflaterOutputStream deflaterOutputStream =
            new DeflaterOutputStream(
                CompressingStreamFactory.maybeWrapStatsOutputStream(
                    outputStream,
                    context,
                    CompressingFilterStats.StatsField.RESPONSE_COMPRESSED_BYTES));
        private final OutputStream statsOutputStream =
            CompressingStreamFactory.maybeWrapStatsOutputStream(
                deflaterOutputStream,
                context,
                CompressingFilterStats.StatsField.RESPONSE_INPUT_BYTES);

        @Override
        public OutputStream getCompressingOutputStream() {
          return statsOutputStream;
        }

        @Override
        public void finish() throws IOException {
          deflaterOutputStream.finish();
        }
      };
    }

    @Override
    CompressingInputStream getCompressingStream(
        final InputStream inputStream, final CompressingFilterContext context) {
      return new CompressingInputStream() {
        @Override
        public InputStream getCompressingInputStream() {
          return CompressingStreamFactory.maybeWrapStatsInputStream(
              new InflaterInputStream(
                  CompressingStreamFactory.maybeWrapStatsInputStream(
                      inputStream,
                      context,
                      CompressingFilterStats.StatsField.REQUEST_COMPRESSED_BYTES)),
              context,
              CompressingFilterStats.StatsField.REQUEST_INPUT_BYTES);
        }
      };
    }
  }
}