Package org.osmorc.impl

Source Code of org.osmorc.impl.BundleCache

package org.osmorc.impl;

import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.framework.Version;
import org.osmorc.manifest.BundleManifest;
import org.osmorc.manifest.ManifestHolder;
import org.osmorc.manifest.ManifestHolderDisposedException;

import java.util.*;

/**
* The bundle cache holds information about all bundles within the project.
*/
class BundleCache {
  private volatile Set<ManifestHolder> myManifestHolders;

  public BundleCache() {
    myManifestHolders = new HashSet<ManifestHolder>();
  }

  /**
   * Clears the bundle cache.
   */
  public synchronized void clear() {
    myManifestHolders = new HashSet<ManifestHolder>();
  }

  /**
   * Updates the cache with the given manifest holder.
   *
   * @param holder the holder
   * @return true, if the holder was added to the cache, false if the holder was already known.
   */
  public synchronized boolean updateWith(@NotNull final ManifestHolder holder) {
    if (!myManifestHolders.contains(holder)) {
      // copy on write
      HashSet<ManifestHolder> copy = new HashSet<ManifestHolder>(myManifestHolders);
      copy.add(holder);
      myManifestHolders = copy;
      return true;
    }
    else {
      return false;
    }
  }

  /**
   * Removes all stale holders from the cache.
   *
   * @return true if there were stale entries, false if nothing changed.
   */
  public synchronized boolean cleanup() {
    Set<ManifestHolder> toRemove = new HashSet<ManifestHolder>();
    for (ManifestHolder manifestHolder : myManifestHolders) {
      if (manifestHolder.isDisposed()) {
        toRemove.add(manifestHolder);
      }
    }
    if (toRemove.isEmpty()) {
      return false;
    }

    // copy on write
    HashSet<ManifestHolder> copy = new HashSet<ManifestHolder>(myManifestHolders);
    copy.removeAll(toRemove);
    myManifestHolders = copy;
    return true;
  }

  /**
   * Returns all manifest holders which provide the given package via export-package.
   *
   * @param packageSpec the package specification (may include version ranges)
   * @return set of matching manifest holders.
   */
  @NotNull
  public Set<ManifestHolder> whoProvides(@NotNull final String packageSpec) {
    Set<ManifestHolder> result = new HashSet<ManifestHolder>();
    for (ManifestHolder manifestHolder : myManifestHolders) {
      BundleManifest bundleManifest;
      try {
        bundleManifest = manifestHolder.getBundleManifest();
      }
      catch (ManifestHolderDisposedException ignore) {
        // ok this thing is gone
        continue;
      }
      if (bundleManifest != null) {
        if (bundleManifest.isPackageExported(packageSpec)) {
          result.add(manifestHolder);
        }
      }
    }
    return result;
  }

  /**
   * Returns a set of  manifest holders that represent fragments of the given manifest holder. Note, that this is a potentially costly operation
   * when there are many manifests.
   *
   * @param bundle the manifest holder to find the fragments for.
   * @return a set of matching manifest holders. If there are no fragments known, returns an empty set.
   */
  @NotNull
  public Set<ManifestHolder> getFragmentsForBundle(@NotNull ManifestHolder bundle) {
    try {
      BundleManifest bundleManifest = bundle.getBundleManifest();
      // if it has no manifest, we can short cut here
      if (bundleManifest == null) {
        return Collections.emptySet();
      }

      Set<ManifestHolder> result = new HashSet<ManifestHolder>();
      for (ManifestHolder manifestHolder : myManifestHolders) {
        try {
          BundleManifest potentialFragmentManifest = manifestHolder.getBundleManifest();
          if (potentialFragmentManifest == null) {
            continue;
          }
          if (bundleManifest.isFragmentHostFor(potentialFragmentManifest)) {
            result.add(manifestHolder);
          }
        }
        catch (ManifestHolderDisposedException ignore) {
          // ignore.
        }
      }
      return result;
    }
    catch (ManifestHolderDisposedException ignore) {
      return Collections.emptySet();
    }
  }

  /**
   * Gets all known fragment hosts for the given fragment.
   *
   * @param fragment the fragment
   * @return a set of fragment hosts. returns an empty set if no hosts could be found or if the given manifest holder does not represent a fragment bundle.
   */
  @NotNull
  public Set<ManifestHolder> getFragmentHosts(@NotNull ManifestHolder fragment) {
    try {
      BundleManifest fragmentManifest = fragment.getBundleManifest();
      // if its not a fragment or has no manifest, we can short cut here
      if (fragmentManifest == null || !fragmentManifest.isFragmentBundle()) {
        return Collections.emptySet();
      }

      Set<ManifestHolder> result = new HashSet<ManifestHolder>();
      for (ManifestHolder manifestHolder : myManifestHolders) {
        try {
          BundleManifest potentialHostManifest = manifestHolder.getBundleManifest();
          if (potentialHostManifest == null) {
            continue;
          }
          if (potentialHostManifest.isFragmentHostFor(fragmentManifest)) {
            result.add(manifestHolder);
          }
        }
        catch (ManifestHolderDisposedException ignore) {
          // ignore.
        }
      }
      return result;
    }
    catch (ManifestHolderDisposedException ignore) {
      return Collections.emptySet();
    }
  }

  /**
   * Returns  the manifest holders that have the given symbolic name.
   *
   * @param bundleSymbolicName the symbolic name
   * @return the matching manifest holders. If no holder matches, returns an empty list.
   */
  @NotNull
  public List<ManifestHolder> whoIs(@NotNull final String bundleSymbolicName) {
    List<ManifestHolder> result = new ArrayList<ManifestHolder>();
    for (ManifestHolder manifestHolder : myManifestHolders) {
      BundleManifest bundleManifest;
      try {
        bundleManifest = manifestHolder.getBundleManifest();
      }
      catch (ManifestHolderDisposedException ignore) {
        continue;
      }
      if (bundleManifest != null) {
        if (bundleSymbolicName.equals(bundleManifest.getBundleSymbolicName())) {
          result.add(manifestHolder);
        }
      }
    }
    return result;
  }

  /**
   * Returns the  manifest holder that confirms to the required-bundle specification. If multiple entities match, the one with the
   * highest version is returned.
   *
   * @param requiredBundleSpec the required bundle specification
   * @return the first matching manifest holder, or null if there is no match
   */
  @Nullable
  public ManifestHolder whoIsRequiredBundle(@NotNull final String requiredBundleSpec) {
    List<ManifestHolder> candidates = new ArrayList<ManifestHolder>();
    for (ManifestHolder manifestHolder : myManifestHolders) {
      BundleManifest bundleManifest;
      try {
        bundleManifest = manifestHolder.getBundleManifest();
      }
      catch (ManifestHolderDisposedException ignore) {
        // this thing is gone
        continue;
      }

      if (bundleManifest != null) {
        if (bundleManifest.isRequiredBundle(requiredBundleSpec)) {
          candidates.add(manifestHolder);
        }
      }
    }

    return getCandidateWithHighestVersion(candidates);
  }


  /**
   * Helper function which takes a collection of manifest holders, creates sets of manifest holders that have the same bundle symbolic name
   * and returns the manifest holder with the highest version for each set.
   *
   * @param manifestHolders the manifest holders
   * @return a set with an entry for each set of manifest holders that have the same bundle symbolic name. The entry is the manifest holder
   *         with the highest version in the respective set. If no highest version can be determined for any given set, then this result
   *         set will not contain an entry for the respective set.
   */
  public static Set<ManifestHolder> getCandidatesWithHighestVersions(@NotNull Collection<ManifestHolder> manifestHolders) {
    Map<String, Set<ManifestHolder>> sets = new HashMap<String, Set<ManifestHolder>>();

    // first build sets of entries with the same bundle symbolic name. This will also eliminate duplicate holders.
    for (ManifestHolder holder : manifestHolders) {
      try {
        BundleManifest manifest = holder.getBundleManifest();
        if (manifest != null) {
          String bundleSymbolicName = manifest.getBundleSymbolicName();
          Set<ManifestHolder> set;
          if (sets.containsKey(bundleSymbolicName)) {
            set = sets.get(bundleSymbolicName);
          }
          else {
            set = new HashSet<ManifestHolder>();
            sets.put(bundleSymbolicName, set);
          }
          set.add(holder);
        }
      }
      catch (ManifestHolderDisposedException e) {
        // its gone, ignore it
      }
    }
    Set<ManifestHolder> result = new HashSet<ManifestHolder>();
    for (Set<ManifestHolder> holders : sets.values()) {
      ManifestHolder candidateWithHighestVersion = getCandidateWithHighestVersion(holders);
      if (candidateWithHighestVersion != null) {
        result.add(candidateWithHighestVersion);
      }
    }

    return result;
  }

  /**
   * Helper method which allows filtering a list of manifest holders and return the candidate with the highest version. It is assumed
   * that the list only contains manifest holders with the same bundle-symbolic name. This method will not check this assumption and will
   * also not depend on it, so the GIGO principle applies.
   *
   * @param candidates the list of candidates
   * @return the candidate with the highest version or null if no such candidate could be determined.
   */
  @Nullable
  public static ManifestHolder getCandidateWithHighestVersion(@NotNull Collection<ManifestHolder> candidates) {
    if (candidates.isEmpty()) {
      return null;
    }

    if (candidates.size() == 1) {
      return candidates.iterator().next();
    }

    ManifestHolder result = null;
    for (ManifestHolder candidate : candidates) {
      if (result == null) {
        result = candidate;
        continue;
      }

      BundleManifest resultManifest;

      try {
        resultManifest = result.getBundleManifest();
        if (resultManifest == null) {
          // weird but may happen
          result = candidate; // discard result and replace it with current candidate.
          continue;
        }
      }
      catch (ManifestHolderDisposedException e) {
        // ok result is gone, replace it with the candidate
        result = candidate;
        continue;
      }

      try {
        BundleManifest bundleManifest = candidate.getBundleManifest();
        if (bundleManifest == null) {
          // weird, but may happen, discard current candidate and go on with the next one
          continue;
        }
        Version candidateVersion = bundleManifest.getBundleVersion();
        Version resultVersion = resultManifest.getBundleVersion();
        if (resultVersion.compareTo(candidateVersion) < 0) { // result version is smaller than candidate version
          result = candidate; // candidate becomes next result
        }
      }
      catch (ManifestHolderDisposedException e) {
        // may happen on rare occasions in which case we ignore that candidate and keep the old result.
      }
    }

    return result;
  }

  /**
   * Returns the manifest holder for the given bundle object.
   *
   * @param bundle the bundle object
   * @return the manifest
   */
  @Nullable
  public ManifestHolder getManifestHolder(@NotNull final Object bundle) {
    for (ManifestHolder manifestHolder : myManifestHolders) {
      Object boundObject;
      try {
        boundObject = manifestHolder.getBoundObject();
      }
      catch (ManifestHolderDisposedException ignore) {
        continue;
      }
      if (boundObject.equals(bundle)) {
        return manifestHolder;
      }
    }
    return null;
  }
}
TOP

Related Classes of org.osmorc.impl.BundleCache

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.