All Downloads are FREE. Search and download functionalities are using the official Maven repository.

proguard.io.DataEntryReaderFactory Maven / Gradle / Ivy

Go to download

ProGuardCORE is a free library to read, analyze, modify, and write Java class files.

There is a newer version: 9.1.6
Show newest version
/*
 * ProGuardCORE -- library to process Java bytecode.
 *
 * Copyright (c) 2002-2022 Guardsquare NV
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package proguard.io;

import static proguard.classfile.ClassConstants.CLASS_FILE_EXTENSION;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import proguard.util.AndMatcher;
import proguard.util.ExtensionMatcher;
import proguard.util.FileNameParser;
import proguard.util.ListFunctionParser;
import proguard.util.ListParser;
import proguard.util.NotMatcher;
import proguard.util.SingleFunctionParser;
import proguard.util.StringFunction;
import proguard.util.StringMatcher;
import proguard.util.WildcardManager;

/**
 * This class can create DataEntryReader instances based on class path entries. The readers will
 * unwrap the input data entries from any jars, wars, ears, jmods, and zips before passing them to a
 * given reader.
 *
 * @author Eric Lafortune
 */
public class DataEntryReaderFactory {

  private static final String VERSIONS_PATTERN = "META-INF/versions";
  private static final String VERSIONS_EXCLUDE = "!META-INF/versions/**";

  private static final String JMOD_CLASS_FILE_PREFIX = "classes/";

  private final boolean android;

  /**
   * Creates a new DataEntryReaderFactory.
   *
   * @param android Specifies whether the packaging is targeted at the Android platform. Archives
   *     inside the assets directory then aren't unpacked but simply read as data files.
   */
  public DataEntryReaderFactory(boolean android) {
    this.android = android;
  }

  /**
   * Creates a DataEntryReader that can read the given class path entry.
   *
   * @param classPathEntry the input class path entry.
   * @param reader a data entry reader to which the reading of actual classes and resource files can
   *     be delegated.
   * @return a DataEntryReader for reading the given class path entry.
   */
  public DataEntryReader createDataEntryReader(
      ClassPathEntry classPathEntry, DataEntryReader reader) {
    boolean isApk = classPathEntry.isApk();
    boolean isAab = classPathEntry.isAab();
    boolean isJar = classPathEntry.isJar();
    boolean isAar = classPathEntry.isAar();
    boolean isWar = classPathEntry.isWar();
    boolean isEar = classPathEntry.isEar();
    boolean isJmod = classPathEntry.isJmod();
    boolean isZip = classPathEntry.isZip();

    List filter = getFilterExcludingVersionedClasses(classPathEntry);
    List apkFilter = classPathEntry.getApkFilter();
    List aabFilter = classPathEntry.getAabFilter();
    List jarFilter = classPathEntry.getJarFilter();
    List aarFilter = classPathEntry.getAarFilter();
    List warFilter = classPathEntry.getWarFilter();
    List earFilter = classPathEntry.getEarFilter();
    List jmodFilter = classPathEntry.getJmodFilter();
    List zipFilter = classPathEntry.getZipFilter();

    // Add a renaming filter, if specified.
    if (filter != null) {
      WildcardManager wildcardManager = new WildcardManager();

      StringFunction nameFunction =
          new ListFunctionParser(
                  new SingleFunctionParser(new FileNameParser(wildcardManager), wildcardManager))
              .parse(filter);

      reader = new RenamedDataEntryReader(nameFunction, reader);
    }

    // Unzip any apks, if necessary.
    reader = wrapInJarReader(reader, false, false, isApk, apkFilter, ".apk");
    if (!isApk) {
      // Unzip any aabs, if necessary.
      reader = wrapInJarReader(reader, false, false, isAab, aabFilter, ".aab");
      if (!isAab) {
        // Unzip any jars, if necessary.
        reader = wrapInJarReader(reader, false, false, isJar, jarFilter, ".jar");
        if (!isJar) {
          // Unzip any aars, if necessary.
          reader = wrapInJarReader(reader, false, false, isAar, aarFilter, ".aar");
          if (!isAar) {
            // Unzip any wars, if necessary.
            reader = wrapInJarReader(reader, true, false, isWar, warFilter, ".war");
            if (!isWar) {
              // Unzip any ears, if necessary.
              reader = wrapInJarReader(reader, false, false, isEar, earFilter, ".ear");
              if (!isEar) {
                // Unzip any jmods, if necessary.
                reader = wrapInJarReader(reader, true, true, isJmod, jmodFilter, ".jmod");
                if (!isJmod) {
                  // Unzip any zips, if necessary.
                  reader = wrapInJarReader(reader, false, false, isZip, zipFilter, ".zip");
                }
              }
            }
          }
        }
      }
    }

    return reader;
  }

  /**
   * Wraps the given DataEntryReader in a JarReader, filtering it if necessary.
   *
   * @param reader the data entry reader that can read the entries contained in the jar file.
   * @param stripClassesPrefix specifies whether to strip the ""classes/" prefix from contained
   *     .class data entries.
   * @param stripJmodHeader specifies whether to strip the jmod magic bytes from the zip.
   * @param isJar specifies whether the data entries should always be unzipped.
   * @param jarFilter otherwise, an optional filter on the data entry names.
   * @param jarExtension also otherwise, a required data entry name extension.
   * @return a DataEntryReader for reading the entries of jar file data entries.
   */
  private DataEntryReader wrapInJarReader(
      DataEntryReader reader,
      boolean stripClassesPrefix,
      boolean stripJmodHeader,
      boolean isJar,
      List jarFilter,
      String jarExtension) {
    if (stripClassesPrefix) {
      reader =
          new FilteredDataEntryReader(
              new DataEntryNameFilter(new ExtensionMatcher(CLASS_FILE_EXTENSION)),
              new PrefixStrippingDataEntryReader(JMOD_CLASS_FILE_PREFIX, reader),
              reader);
    }

    // Unzip any jars, if necessary.
    DataEntryReader jarReader = new JarReader(stripJmodHeader, reader);

    if (isJar) {
      // Always unzip.
      return jarReader;
    } else {
      // Add a filter, if specified.
      if (jarFilter != null) {
        jarReader =
            new FilteredDataEntryReader(
                new DataEntryNameFilter(new ListParser(new FileNameParser()).parse(jarFilter)),
                jarReader);
      }

      StringMatcher jarMatcher = new ExtensionMatcher(jarExtension);

      // Don't unzip archives in Android assets directories.
      if (android) {
        jarMatcher =
            new AndMatcher(
                new AndMatcher(
                    new NotMatcher(
                        new ListParser(new FileNameParser()).parse("assets/**,*/assets/**")),
                    new NotMatcher(new ListParser(new FileNameParser()).parse("res/**,*/res/**"))),
                jarMatcher);
      }

      // Only unzip the right type of jars.
      return new FilteredDataEntryReader(new DataEntryNameFilter(jarMatcher), jarReader, reader);
    }
  }

  /**
   * Method to return an augmented filter for supported features.
   *
   * 

Currently versioned class files (a feature introduced in Java 9) are not fully supported by * ProGuard. Only 1 version of a class can be read and processed. If no custom filter targeting a * specific version is used, exclude such classes from being read. */ public static List getFilterExcludingVersionedClasses(ClassPathEntry classPathEntry) { List originalFilter = classPathEntry.getFilter(); if (originalFilter == null) { return Collections.singletonList(VERSIONS_EXCLUDE); } else { // If there is already a custom filter for versioned classes // assume that the filter is properly setup. for (String element : originalFilter) { if (element.contains(VERSIONS_PATTERN)) { return originalFilter; } } // Otherwise, exclude all versioned classes. List filter = new ArrayList<>(); filter.add(VERSIONS_EXCLUDE); filter.addAll(originalFilter); return filter; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy