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

grim.asserts.RuleLoader Maven / Gradle / Ivy

package grim.asserts;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import java.util.zip.ZipFile;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.lang.model.SourceVersion;

final class RuleLoader
{
  @Nonnull
  private static final String BASE_PATH = "META-INF/grim";
  @Nonnull
  private static final String FILE_SUFFIX = ".grim.json";
  @Nonnull
  private static final Predicate GRIM_FILE_MATCHER = r -> r.endsWith( FILE_SUFFIX );

  private RuleLoader()
  {
  }

  @Nonnull
  static Collection loadFromArchive( @Nonnull final Path archivePath,
                                           @Nullable final Predicate filterFn )
  {
    try
    {
      final JarFile jarFile = new JarFile( archivePath.toFile(), true, ZipFile.OPEN_READ );
      final Predicate filter = asFilter( filterFn );
      final List rules = new ArrayList<>();
      for ( final JarEntry entry : jarFile.stream().collect( Collectors.toList() ) )
      {
        if ( entry.getName().startsWith( BASE_PATH ) && filter.test( entry.getName() ) )
        {
          rules.addAll( loadOmitRules( jarFile.getInputStream( entry ) ) );
        }
      }
      return rules;
    }
    catch ( final IOException ioe )
    {
      throw new IllegalStateException( "Failed to load Grim Omit rules from " + archivePath, ioe );
    }
  }

  @Nonnull
  static Collection loadFromClassLoader( @Nonnull final ClassLoader classLoader,
                                               @Nullable final Predicate filterFn )
  {
    final List resourceNames = new ArrayList<>();
    collectResourceNames( classLoader, BASE_PATH, asFilter( filterFn ), resourceNames );

    final List rules = new ArrayList<>();
    for ( final String resourceName : resourceNames )
    {
      try
      {
        final InputStream resourceStream = classLoader.getResourceAsStream( resourceName );
        if ( null == resourceStream )
        {
          throw new IOException( "Failed to locate grim rules for resource " + resourceName );
        }
        rules.addAll( loadOmitRules( resourceStream ) );
      }
      catch ( final IOException ioe )
      {
        throw new IllegalStateException( "Failed to load Grim Omit rules from " + resourceName, ioe );
      }
    }
    return rules;
  }

  private static void collectResourceNames( @Nonnull final ClassLoader classLoader,
                                            @Nonnull final String base,
                                            @Nonnull final Predicate acceptResource,
                                            @Nonnull final List resources )
  {
    final InputStream resourceStream = classLoader.getResourceAsStream( base );
    if ( null != resourceStream )
    {
      try ( final BufferedReader reader = toReader( resourceStream ) )
      {
        String line;
        while ( null != ( line = reader.readLine() ) )
        {
          final String resource = base + "/" + line;
          if ( acceptResource.test( resource ) )
          {
            resources.add( resource );
          }
          else if ( SourceVersion.isIdentifier( line ) )
          {
            collectResourceNames( classLoader, resource, acceptResource, resources );
          }
        }
      }
      catch ( final IOException ioe )
      {
        throw new IllegalStateException( "Failed to read grim metadata directory", ioe );
      }
    }
  }

  @Nonnull
  private static List loadOmitRules( @Nonnull final InputStream inputStream )
    throws IOException
  {
    final List rules = new ArrayList<>();
    try ( final JsonReader reader = Json.createReader( inputStream ) )
    {
      final JsonArray ruleArray = reader.readArray();
      final int size = ruleArray.size();
      for ( int i = 0; i < size; i++ )
      {
        rules.add( parseOmitRule( i, ruleArray.getJsonObject( i ) ) );
      }
    }
    return rules;
  }

  @Nonnull
  private static Rule parseOmitRule( final int ruleIndex, @Nonnull final JsonObject ruleObject )
    throws IOException
  {
    final String type = ruleObject.getString( "type" );
    final boolean omit = !ruleObject.getBoolean( "keep", false );
    final String member = ruleObject.getString( "member", null );
    final String property = ruleObject.getString( "property", null );
    final String value = ruleObject.getString( "value", null );
    final String operator = ruleObject.getString( "operator", null );
    if ( ( null != property || null != value || null != operator ) &&
         ( null == property || null == value || null == operator ) )
    {
      throw new IOException( "Grim rule at index " + ruleIndex + " contains a partially defined operator" );
    }
    try
    {
      return new Rule( omit,
                       Pattern.compile( type ),
                       null == member ? null : Pattern.compile( member ),
                       null == property ?
                       null :
                       new Condition( property, value, operator.equals( "EQ" ) ) );
    }
    catch ( final PatternSyntaxException pse )
    {
      throw new IOException( "Grim rule at index " + ruleIndex + " contains a pattern with a syntax error", pse );
    }
  }

  @Nonnull
  private static Predicate asFilter( @Nullable final Predicate filterFn )
  {
    return null == filterFn ?
           GRIM_FILE_MATCHER :
           GRIM_FILE_MATCHER.and( resource -> {
             final int beginIndex = BASE_PATH.length() + 1;
             final int endIndex = resource.length() - FILE_SUFFIX.length();
             return filterFn.test( resource.substring( beginIndex, endIndex ).replace( "/", "." ) );
           } );
  }

  @Nonnull
  private static BufferedReader toReader( @Nonnull final InputStream resourceStream )
  {
    return new BufferedReader( new InputStreamReader( resourceStream, StandardCharsets.UTF_8 ) );
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy