Class Analyzer

  • All Implemented Interfaces:
    Constants, Registry, Report, Reporter, java.io.Closeable, java.lang.AutoCloseable, java.lang.Iterable<java.lang.String>
    Direct Known Subclasses:
    Builder

    public class Analyzer
    extends Processor
    This class can calculate the required headers for a (potential) JAR file. It analyzes a directory or JAR for the packages that are contained and that are referred to by the bytecodes. The user can the use regular expressions to define the attributes and directives. The matching is not fully regex for convenience. A * and ? get a . prefixed and dots are escaped.
     *;auto=true             any
     org.acme.*;auto=true    org.acme.xyz
     org.[abc]*;auto=true    org.acme.xyz
     
    Additional, the package instruction can start with a '=' or a '!'. The '!' indicates negation. Any matching package is removed. The '=' is literal, the expression will be copied verbatim and no matching will take place. Any headers in the given properties are used in the output properties.
    • Field Detail

      • logger

        private static final org.slf4j.Logger logger
      • frameworkPreR7

        private static final VersionRange frameworkPreR7
      • ees

        private final java.util.SortedSet<Clazz.JAVA> ees
      • dot

        private Jar dot
      • contained

        private final Packages contained
      • referred

        private final Packages referred
      • contracts

        private final Contracts contracts
      • classpathExports

        private final Packages classpathExports
      • classpath

        private final java.util.List<Jar> classpath
      • analyzed

        private boolean analyzed
      • diagnostics

        private boolean diagnostics
      • inited

        private boolean inited
      • OLD_PACKAGEINFO_SYNTAX_P

        static final java.util.regex.Pattern OLD_PACKAGEINFO_SYNTAX_P
      • OBJECT_REFERENCE

        static final java.util.regex.Pattern OBJECT_REFERENCE
      • firstUse

        boolean firstUse
      • fuzzyVersion

        static final java.util.regex.Pattern fuzzyVersion
        Clean up version parameters. Other builders use more fuzzy definitions of the version syntax. This method cleans up such a version to match an OSGi version.
      • fuzzyVersionRange

        static final java.util.regex.Pattern fuzzyVersionRange
      • fuzzyModifier

        static final java.util.regex.Pattern fuzzyModifier
      • DEFAULT_PROVIDER_POLICY

        static final java.lang.String DEFAULT_PROVIDER_POLICY
        See Also:
        Constant Field Values
      • DEFAULT_CONSUMER_POLICY

        static final java.lang.String DEFAULT_CONSUMER_POLICY
        See Also:
        Constant Field Values
      • _classesHelp

        static final java.lang.String _classesHelp
        The extends macro traverses all classes and returns a list of class names that extend a base class.
      • _packagesHelp

        static final java.lang.String _packagesHelp
      • BASE64HEX_P

        private static final java.util.regex.Pattern BASE64HEX_P
        md5 macro
    • Constructor Detail

      • Analyzer

        public Analyzer​(Jar jar)
                 throws java.lang.Exception
        Throws:
        java.lang.Exception
      • Analyzer

        public Analyzer​(Processor parent)
      • Analyzer

        public Analyzer()
    • Method Detail

      • getManifest

        public static java.util.Properties getManifest​(java.io.File dirOrJar)
                                                throws java.lang.Exception
        Specifically for Maven
        Throws:
        java.lang.Exception
      • analyze

        public void analyze()
                     throws java.lang.Exception
        Calculates the data structures for generating a manifest.
        Throws:
        java.io.IOException
        java.lang.Exception
      • getHostPackages

        public java.util.Optional<java.util.Set<Descriptors.PackageRef>> getHostPackages()
        Get the packages from the host if this is a fragment bundle
        Returns:
        the host packages or an empty set if not a fragment
      • getRequireBundlePackages

        public java.util.Optional<java.util.Set<Descriptors.PackageRef>> getRequireBundlePackages()
        Find the packages belonging to the required bundles
        Returns:
        the packages from the required bundles, with no Require-Bundle return an empty Optional
      • toJar

        private Jar toJar​(java.util.Map.Entry<java.lang.String,​Attrs> host)
      • getExportedByAnnotation

        private Parameters getExportedByAnnotation()
      • doConditionalPackages

        private void doConditionalPackages()
                                    throws java.lang.Exception
        Throws:
        java.lang.Exception
      • learnPackage

        private void learnPackage​(Jar jar,
                                  java.lang.String prefix,
                                  Descriptors.PackageRef packageRef,
                                  Packages map)
                           throws java.lang.Exception
        Throws:
        java.lang.Exception
      • getName

        protected java.lang.String getName​(Jar jar)
                                    throws java.lang.Exception
        Throws:
        java.lang.Exception
      • parsePackageInfoClass

        private Attrs parsePackageInfoClass​(Resource r)
                                     throws java.lang.Exception
        Throws:
        java.lang.Exception
      • removeDynamicImports

        void removeDynamicImports​(Packages referredAndExported)
        Discussed with BJ and decided to kill the .
        Parameters:
        referredAndExported -
      • getExtra

        protected Jar getExtra()
                        throws java.lang.Exception
        Throws:
        java.lang.Exception
      • doPlugins

        void doPlugins()
      • isResourceOnly

        boolean isResourceOnly()
        Returns:
        true if the -resourceonly instruction is set, false otherwise
      • calcManifest

        public java.util.jar.Manifest calcManifest()
                                            throws java.lang.Exception
        One of the main workhorses of this class. This will analyze the current setup and calculate a new manifest according to this setup.
        Throws:
        java.io.IOException
        java.lang.Exception
      • findClasspathEntry

        public Jar findClasspathEntry​(java.lang.String bsn,
                                      java.lang.String r)
        Find a class path entry based on bsn and versionrange
        Parameters:
        bsn - The bundle symbolic name
        r - The version range specified like in OSGi (version => [version,infinite))
        Returns:
        first JAR that matches bsn &r or null if not found
      • doEEProfiles

        private java.lang.String doEEProfiles​(Clazz.JAVA highest)
                                       throws java.io.IOException
        Added for 1.8 profiles. A 1.8 profile is a set of packages so the VM can be delivered in smaller versions. This method will look at the Constants.EEPROFILE option. If it is set, it can be "auto" or it can contain a list of profiles specified as name="a,b,c" values. If we find a package outside the profiles, no profile is set. Otherwise the highest found profile is added. This only works for java packages.
        Throws:
        java.io.IOException
      • doHeader

        private void doHeader​(java.util.jar.Attributes main,
                              java.lang.String header)
      • doNamesection

        private void doNamesection​(Jar dot,
                                   java.util.jar.Manifest manifest)
        Parse the namesection as instructions and then match them against the current set of resources For example:
          -namesection: *;baz=true,
         abc/def/bar/X.class=3
         
        The raw value of Constants.NAMESECTION is used but the values of the attributes are replaced where @ is set to the resource name. This allows macro to operate on the resource
      • doNameSection

        void doNameSection​(java.util.jar.Manifest manifest,
                           java.lang.String header)
        This method is called when the header starts with a @, signifying a name section header. The name part is defined by replacing all the @ signs to a /, removing the first and the last, and using the last part as header name:
          @org@osgi@service@event@Implementation-Title
         
        This will be the header Implementation-Title in the org/osgi/service/event named section.
        Parameters:
        manifest -
        header -
      • getBsn

        public java.lang.String getBsn()
        Clear the key part of a header. I.e. remove everything from the first ';'
      • _bsn

        public java.lang.String _bsn​(java.lang.String[] args)
      • calculateExportsFromContents

        public java.lang.String calculateExportsFromContents​(Jar bundle)
        Calculate an export header solely based on the contents of a JAR file
        Parameters:
        bundle - The jar file to analyze
      • getContained

        public Packages getContained()
      • getExports

        public Packages getExports()
      • getImports

        public Packages getImports()
      • getJar

        public Jar getJar()
      • getReferred

        public Packages getReferred()
      • getUnreachable

        public java.util.Set<Descriptors.PackageRef> getUnreachable()
        Return the set of unreachable code depending on exports and the bundle activator.
      • getClasspathExports

        public Packages getClasspathExports()
      • getBndVersion

        public java.lang.String getBndVersion()
        Get the version for this bnd
        Returns:
        version or unknown.
      • getBndLastModified

        public long getBndLastModified()
      • getBndInfo

        public java.lang.String getBndInfo​(java.lang.String key,
                                           java.lang.String defaultValue)
      • mergeManifest

        public void mergeManifest​(java.util.jar.Manifest manifest)
                           throws java.io.IOException
        Merge the existing manifest with the instructions but do not override existing properties.
        Parameters:
        manifest - The manifest to merge with
        Throws:
        java.io.IOException
      • setBase

        public void setBase​(java.io.File file)
        Overrides:
        setBase in class Processor
      • setClasspath

        public void setClasspath​(java.util.Collection<?> classpath)
                          throws java.io.IOException
        Set the classpath for this analyzer by file.
        Parameters:
        classpath -
        Throws:
        java.io.IOException
      • setClasspath

        public void setClasspath​(java.io.File[] classpath)
                          throws java.io.IOException
        Throws:
        java.io.IOException
      • setClasspath

        public void setClasspath​(Jar[] classpath)
      • setClasspath

        public void setClasspath​(java.lang.String[] classpath)
      • setJar

        public Jar setJar​(java.io.File file)
                   throws java.io.IOException
        Set the JAR file we are going to work in. This will read the JAR in memory.
        Parameters:
        file -
        Throws:
        java.io.IOException
      • setJar

        public Jar setJar​(Jar jar)
        Set the JAR directly we are going to work on.
        Parameters:
        jar -
      • begin

        protected void begin()
        Overrides:
        begin in class Processor
      • getJarFromName

        public Jar getJarFromName​(java.lang.String name,
                                  java.lang.String from)
        Try to get a Jar from a file name/path or a url, or in last resort from the classpath name part of their files.
        Overrides:
        getJarFromName in class Processor
        Parameters:
        name - URL or filename relative to the base
        from - Message identifying the caller for errors
        Returns:
        null or a Jar with the contents for the name
      • getJarsFromName

        public java.util.List<Jar> getJarsFromName​(java.lang.String name,
                                                   java.lang.String from)
      • jarsFromName

        private java.util.stream.Stream<Jar> jarsFromName​(java.lang.String name,
                                                          java.lang.String from)
      • merge

        private void merge​(java.util.jar.Manifest result,
                           java.util.jar.Manifest old)
      • verifyManifestHeadersCase

        void verifyManifestHeadersCase​(java.util.Properties properties)
        Bnd is case sensitive for the instructions so we better check people are not using an invalid case. We do allow this to set headers that should not be processed by us but should be used by the framework.
        Parameters:
        properties - Properties to verify.
      • doExportsToImports

        Packages doExportsToImports​(Packages exports)
        We will add all exports to the imports unless there is a -noimport directive specified on an export. This directive is skipped for the manifest. We also remove any version parameter so that augmentImports can do the version policy. The following method is really tricky and evolved over time. Coming from the original background of OSGi, it was a weird idea for me to have a public package that should not be substitutable. I was so much convinced that this was the right rule that I rücksichtlos imported them all. Alas, the real world was more subtle than that. It turns out that it is not a good idea to always import. First, there must be a need to import, i.e. there must be a contained package that refers to the exported package for it to make use importing that package. Second, if an exported package refers to an internal package than it should not be imported. Additionally, it is necessary to treat the exports in groups. If an exported package refers to another exported packages than it must be in the same group. A framework can only substitute exports for imports for the whole of such a group. WHY????? Not clear anymore ...
      • getManifestInfoFromClasspath

        private void getManifestInfoFromClasspath​(Jar jar,
                                                  Packages classpathExports,
                                                  Contracts contracts)
      • fixupOldStyleVersions

        private void fixupOldStyleVersions​(Attrs attrs)
      • augmentImports

        void augmentImports​(Packages imports,
                            Packages exports)
                     throws java.lang.Exception
        Find some more information about imports in manifest and other places. It is assumed that the augmentsExports has already copied external attrs from the classpathExports.
        Throws:
        java.lang.Exception
      • applyVersionPolicy

        java.lang.String applyVersionPolicy​(java.lang.String exportVersion,
                                            java.lang.String importRange,
                                            boolean provider)
      • findProvidedPackages

        java.util.Set<Descriptors.PackageRef> findProvidedPackages()
                                                            throws java.lang.Exception
        Find the packages we depend on, where we implement an interface that is a Provider Type. These packages, when we import them, must use the provider policy.
        Throws:
        java.lang.Exception
      • augmentExports

        void augmentExports​(Packages exports)
                     throws java.io.IOException
        Provide any macro substitutions and versions for exported packages.
        Throws:
        java.io.IOException
      • fixupAttributes

        void fixupAttributes​(Descriptors.PackageRef packageRef,
                             Attrs attributes)
                      throws java.io.IOException
        Fixup Attributes Execute any macros on an export and
        Throws:
        java.io.IOException
      • removeAttributes

        void removeAttributes​(Attrs attributes)
        Remove the attributes mentioned in the REMOVE_ATTRIBUTE_DIRECTIVE. You can add a remove-attribute: directive with a regular expression for attributes that need to be removed. We also remove all attributes that have a value of !. This allows you to use macros with ${if} to remove values.
      • calculateVersionRange

        java.lang.String calculateVersionRange​(java.lang.String version,
                                               boolean impl)
        Calculate a version from a version policy.
        Parameters:
        version - The actual exported version
        impl - true for implementations and false for clients
      • removeTransitive

        void removeTransitive​(Descriptors.PackageRef name,
                              java.util.Set<Descriptors.PackageRef> unreachable)
        Transitively remove all elemens from unreachable through the uses link.
        Parameters:
        name -
        unreachable -
      • verifyAttribute

        private void verifyAttribute​(java.lang.String path,
                                     java.lang.String where,
                                     java.lang.String key,
                                     java.lang.String value)
                              throws java.io.IOException
        Throws:
        java.io.IOException
      • close

        public void close()
                   throws java.io.IOException
        Specified by:
        close in interface java.lang.AutoCloseable
        Specified by:
        close in interface java.io.Closeable
        Overrides:
        close in class Processor
        Throws:
        java.io.IOException
      • _findpath

        public java.lang.String _findpath​(java.lang.String[] args)
        Findpath looks through the contents of the JAR and finds paths that end with the given regular expression ${findpath (; reg-expr (; replacement)? )? }
        Parameters:
        args -
      • _findname

        public java.lang.String _findname​(java.lang.String[] args)
      • findPath

        java.lang.String findPath​(java.lang.String name,
                                  java.lang.String[] args,
                                  boolean fullPathName)
      • putAll

        public void putAll​(java.util.Map<java.lang.String,​java.lang.String> additional,
                           boolean force)
      • getClasspath

        public java.util.List<Jar> getClasspath()
      • addClasspath

        public void addClasspath​(Jar jar)
      • addClasspath

        public void addClasspath​(java.util.Collection<?> jars)
                          throws java.io.IOException
        Throws:
        java.io.IOException
      • addClasspath

        public void addClasspath​(java.io.File cp)
                          throws java.io.IOException
        Throws:
        java.io.IOException
      • getTarget

        public Jar getTarget()
      • analyzeBundleClasspath

        private void analyzeBundleClasspath()
                                     throws java.lang.Exception
        Throws:
        java.lang.Exception
      • analyzeJar

        private boolean analyzeJar​(Jar jar,
                                   java.lang.String prefix,
                                   boolean okToIncludeDirs,
                                   java.lang.String bcpEntry)
                            throws java.lang.Exception
        We traverse through all the classes that we can find and calculate the contained and referred set and uses. This method ignores the Bundle classpath.
        Throws:
        java.lang.Exception
      • cleanupVersion

        public static java.lang.String cleanupVersion​(java.lang.String version)
      • isInteger

        private static boolean isInteger​(java.lang.String minor)
        TRhe cleanup version got confused when people used numeric dates like 201209091230120 as qualifiers. These are too large for Integers. This method checks if the all digit string fits in an integer.
          maxint =
         2,147,483,647 = 10 digits
         
        Returns:
        if this fits in an integer
      • removeLeadingZeroes

        private static java.lang.String removeLeadingZeroes​(java.lang.String group)
      • cleanupModifier

        static void cleanupModifier​(java.lang.StringBuilder result,
                                    java.lang.String modifier)
      • getVersionPolicy

        public java.lang.String getVersionPolicy​(boolean implemented)
      • _classes

        public java.lang.String _classes​(java.lang.String... args)
                                  throws java.lang.Exception
        Throws:
        java.lang.Exception
      • getClasses

        public java.util.Collection<Clazz> getClasses​(java.lang.String... args)
                                               throws java.lang.Exception
        Throws:
        java.lang.Exception
      • _packages

        public java.lang.String _packages​(java.lang.String... args)
                                   throws java.lang.Exception
        Throws:
        java.lang.Exception
      • getPackages

        public java.util.Collection<Descriptors.PackageRef> getPackages​(Packages scope,
                                                                        java.lang.String... args)
                                                                 throws java.lang.Exception
        Throws:
        java.lang.Exception
      • _exporters

        public java.lang.String _exporters​(java.lang.String[] args)
                                    throws java.lang.Exception
        Get the exporter of a package ...
        Throws:
        java.lang.Exception
      • _packageattribute

        public java.lang.String _packageattribute​(java.lang.String[] args)
        Return an attribute of a package
      • findResource

        public Resource findResource​(java.lang.String path)
        Locate a resource on the class path.
        Parameters:
        path - Path of the reosurce
        Returns:
        A resource or null
      • findResources

        public java.util.stream.Stream<Resource> findResources​(java.util.function.Predicate<java.lang.String> matches)
      • findClass

        public Clazz findClass​(Descriptors.TypeRef typeRef)
                        throws java.lang.Exception
        Find a clazz on the class path. This class has been parsed.
        Throws:
        java.lang.Exception
      • getVersion

        public java.lang.String getVersion()
        Answer the bundle version.
      • isNoBundle

        public boolean isNoBundle()
      • referToByBinaryName

        public void referToByBinaryName​(java.lang.String binaryClassName)
      • doRequireBnd

        protected void doRequireBnd()
        Ensure that we are running on the correct bnd.
      • _md5

        public java.lang.String _md5​(java.lang.String[] args)
                              throws java.lang.Exception
        Throws:
        java.lang.Exception
      • _sha1

        public java.lang.String _sha1​(java.lang.String[] args)
                               throws java.lang.Exception
        Throws:
        java.lang.Exception
      • getTypeRefFromPath

        public Descriptors.TypeRef getTypeRefFromPath​(java.lang.String path)
      • getClassSignature

        public ClassSignature getClassSignature​(java.lang.String signature)
      • getMethodSignature

        public MethodSignature getMethodSignature​(java.lang.String signature)
      • getFieldSignature

        public FieldSignature getFieldSignature​(java.lang.String signature)
      • filter

        Packages filter​(Instructions instructions,
                        Packages source,
                        java.util.Set<Instruction> nomatch)
        Merge the attributes of two maps, where the first map can contain wildcarded names. The idea is that the first map contains instructions (for example *) with a set of attributes. These patterns are matched against the found packages in actual. If they match, the result is set with the merged set of attributes. It is expected that the instructions are ordered so that the instructor can define which pattern matches first. Attributes in the instructions override any attributes from the actual.
        A pattern is a modified regexp so it looks like globbing. The * becomes a .* just like the ? becomes a .?. '.' are replaced with \\. Additionally, if the pattern starts with an exclamation mark, it will remove that matches for that pattern (- the !) from the working set. So the following patterns should work:
        • com.foo.bar
        • com.foo.*
        • com.foo.???
        • com.*.[^b][^a][^r]
        • !com.foo.* (throws away any match for com.foo.*)
        Enough rope to hang the average developer I would say.
        Parameters:
        instructions - the instructions with patterns.
        source - the actual found packages, contains no duplicates
        Returns:
        Only the packages that were filtered by the given instructions
      • setDiagnostics

        public void setDiagnostics​(boolean b)
      • _ee

        public java.lang.String _ee​(java.lang.String[] args)
      • getOutputFile

        public java.io.File getOutputFile​(java.lang.String output)
        Calculate the output file for the given target. The strategy is:
         parameter given if not null and not directory if directory, this will be
         the output directory based on bsn-version.jar name of the source file if
         exists Untitled-[n]
         
        Parameters:
        output - may be null, otherwise a file path relative to base
      • save

        public boolean save​(java.io.File output,
                            boolean force)
                     throws java.lang.Exception
        Utility function to carefully save the file. Will create a backup if the source file has the same path as the output. It will also only save if the file was modified or the force flag is true
        Parameters:
        output - the output file, if null getOutputFile(String) is used.
        force - if it needs to be overwritten
        Throws:
        java.lang.Exception
      • setDefaults

        public void setDefaults​(java.lang.String bsn,
                                Version version)
        Set default import and export instructions if none are set
      • getClassspace

        public java.util.Set<Clazz> getClassspace​(Descriptors.PackageRef source)
        Return the classes for a given source package.
        Parameters:
        source - the source package
        Returns:
        a set of classes for the requested package.
      • getXRef

        public java.util.Map<Clazz.Def,​java.util.List<Descriptors.TypeRef>> getXRef​(Descriptors.PackageRef source,
                                                                                          java.util.Collection<Descriptors.PackageRef> dest,
                                                                                          int sourceModifiers)
                                                                                   throws java.lang.Exception
        Create a cross reference from package source, to packages in dest
        Parameters:
        source -
        dest -
        sourceModifiers -
        Throws:
        java.lang.Exception
      • _exports

        public java.lang.String _exports​(java.lang.String[] args)
      • _imports

        public java.lang.String _imports​(java.lang.String[] args)
      • filter

        private <T> java.util.Collection<T> filter​(java.util.Collection<T> list,
                                                   java.lang.String[] args)
      • report

        public void report​(java.util.Map<java.lang.String,​java.lang.Object> table)
                    throws java.lang.Exception
        Report the details of this analyzer
        Overrides:
        report in class Processor
        Throws:
        java.lang.Exception
      • getEEs

        public java.util.SortedSet<Clazz.JAVA> getEEs()
        Return the EEs
      • validResourcePath

        public java.lang.String validResourcePath​(java.lang.String name,
                                                  java.lang.String reportIfWrong)
        Parameters:
        name -
      • check

        public boolean check​(Analyzer.Check key)
        Check if we have an a check option
      • getSourceFileFor

        public java.lang.String getSourceFileFor​(Descriptors.TypeRef type)
                                          throws java.lang.Exception
        Find the source file for this type
        Parameters:
        type -
        Throws:
        java.lang.Exception
      • getSourceFileFor

        public java.lang.String getSourceFileFor​(Descriptors.TypeRef type,
                                                 java.util.Collection<java.io.File> sourcePath)
                                          throws java.lang.Exception
        Throws:
        java.lang.Exception
      • setTypeLocation

        public void setTypeLocation​(Reporter.SetLocation location,
                                    Descriptors.TypeRef type)
                             throws java.lang.Exception
        Set location information for a type.
        Throws:
        java.lang.Exception
      • assignable

        public boolean assignable​(java.lang.String annoService,
                                  java.lang.String inferredService)
      • assignable

        public boolean assignable​(java.lang.String annoService,
                                  java.lang.String inferredService,
                                  boolean unknownResult)
      • assignable

        public boolean assignable​(Clazz annoServiceClazz,
                                  Clazz inferredServiceClazz)
      • assignable

        public boolean assignable​(Clazz annoServiceClazz,
                                  Clazz inferredServiceClazz,
                                  boolean unknownResult)
      • getBundleClassPathEntry

        public java.util.Optional<java.lang.String> getBundleClassPathEntry​(Clazz clazz)
      • assignable0

        private java.lang.Boolean assignable0​(Clazz annoServiceClazz,
                                              Clazz inferredServiceClazz)
                                       throws java.lang.Exception
        Throws:
        java.lang.Exception
      • addDefinedContracts

        private void addDefinedContracts()