text:
<?php namespace Nymph;

class Entity implements EntityInterface {
  const ETYPE = 'entity';

  /**
   * The entity's Globally Unique ID.
   *
   * @var int|null
   * @access public
   */
  private $guid = null;
  /**
   * The creation date of the entity as a high precision Unix timestamp.
   *
   * The value is rounded to the ten thousandths digit.
   *
   * @var float|null
   * @access public
   */
  private $cdate = null;
  /**
   * The modified date of the entity as a high precision Unix timestamp.
   *
   * The value is rounded to the ten thousandths digit.
   *
   * @var float|null
   * @access public
   */
  private $mdate = null;
  /**
   * Array of the entity's tags.
   *
   * @var array
   * @access public
   */
  protected $tags = [];
  /**
   * The data store.
   *
   * @var array
   */
  protected $data;
  /**
   * Whether this instance is a sleeping reference.
   *
   * @var bool
   */
  private $isASleepingReference = false;
  /**
   * The reference to use to wake.
   *
   * @var array|null
   */
  private $sleepingReference = null;
  /**
   * Properties that should be converted to standard objects instead of arrays
   * when unserializing from JSON.
   *
   * @var array
   */
  public $objectData = [];
  /**
   * Properties that will not be serialized into JSON with json_encode(). This
   * can be considered a blacklist, because these properties will not be set
   * with incoming JSON.
   *
   * Clients CAN still determine what is in these properties, unless they are
   * also listed in searchRestrictedData.
   *
   * @var array
   */
  protected $privateData = [];
  /**
   * Properties that will not be searchable from the frontend. If the frontend
   * includes any of these properties in any of their clauses, they will be
   * filtered out before the search is executed.
   *
   * @var array
   */
  public static $searchRestrictedData = [];
  /**
   * Properties that can only be modified by server side code. They will still
   * be visible on the frontend, unlike privateData, but any changes to them
   * that come from the frontend will be ignored.
   *
   * In addition to what's listed here, the 'user' and 'group' properties will
   * be filtered for non-admins when Tilmeld is detected.
   *
   * @var array
   */
  protected $protectedData = [];
  /**
   * If this is an array, then it lists the only properties that will be
   * accepted from incoming JSON. Any other properties will be ignored.
   *
   * If you use a whitelist, you don't need to use protectedData, since you
   * can simply leave those entries out of whitelistData.
   *
   * @var array|bool
   */
  protected $whitelistData = false;
  /**
   * Tags that can only be added/removed by server side code. They will still be
   * visible on the frontend, but any changes to them that come from the
   * frontend will be ignored.
   *
   * @var array
   */
  protected $protectedTags = [];
  /**
   * If this is an array, then it lists the only tags that will be accepted from
   * incoming JSON. Any other tags will be ignored.
   *
   * @var array|bool
   */
  protected $whitelistTags = false;
  /**
   * The names of methods allowed to be called by the frontend with serverCall.
   *
   * @var array
   */
  protected $clientEnabledMethods = [];
  /**
   * The names of static methods allowed to be called by the frontedn with
   * serverCallStatic.
   *
   * @var array
   */
  public static $clientEnabledStaticMethods = [];
  /**
   * Whether to use "skip_ac" when accessing entity references.
   *
   * @var bool
   */
  private $useSkipAc = false;
  /**
   * The AC properties' values when the entity was loaded.
   *
   * @var array
   */
  private $originalAcValues = [];

  /**
   * Load an entity.
   * @param int $id The ID of the entity to load, 0 for a new entity.
   */
  public function __construct($id = 0) {
    if (!isset($this->data)) {
      $this->data = new EntityData();
    }
    if ($id > 0) {
      $entity = Nymph::getEntity(
        ['class' => get_class($this)],
        ['&', 'guid' => $id]
      );
      if (isset($entity)) {
        $this->guid = $entity->guid;
        $this->tags = $entity->tags;
        $this->cdate = $entity->cdate;
        $this->mdate = $entity->mdate;
        $this->putData($entity->getData(), $entity->getSData());
        return $this;
      }
    }
    return null;
  }

  /**
   * Create a new instance.
   * @return \Nymph\Entity The new instance.
   */
  public static function factory() {
    $className = get_called_class();
    $args = func_get_args();
    $reflector = new \ReflectionClass($className);
    $entity = $reflector->newInstanceArgs($args);
    // Use hook functionality when available.
    if (class_exists('\SciActive\Hook')) {
      \SciActive\Hook::hookObject($entity, $className.'->', false);
    }
    return $entity;
  }

  /**
   * Create a new sleeping reference instance.
   *
   * Sleeping references won't retrieve their data from the database until it
   * is actually used.
   *
   * @param array $reference The Nymph Entity Reference to use to wake.
   * @return \Nymph\Entity The new instance.
   */
  public static function factoryReference($reference) {
    $className = $reference[2];
    if (!class_exists($className)) {
      throw new Exceptions\EntityClassNotFoundException(
        "factoryReference called for a class that can't be found, $className."
      );
    }
    $entity = call_user_func([$className, 'factory']);
    $entity->referenceSleep($reference);
    return $entity;
  }

  /**
   * Retrieve a property.
   *
   * You do not need to explicitly call this method. It is called by PHP when
   * you access the property normally.
   *
   * @param string $name The name of the property.
   * @return mixed The value of the property or null if it doesn't exist.
   */
  public function &__get($name) {
    if ($name === 'guid') {
      return $this->guid;
    }
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    if ($name === 'cdate'
      || $name === 'mdate'
      || $name === 'tags'
    ) {
      return $this->$name;
    }
    return $this->data->$name;
  }

  /**
   * Checks whether a property is set.
   *
   * You do not need to explicitly call this method. It is called by PHP when
   * you access the property normally.
   *
   * @param string $name The name of the property.
   * @return bool
   */
  public function __isset($name) {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    if ($name === 'guid'
      || $name === 'cdate'
      || $name === 'mdate'
      || $name === 'tags'
    ) {
      return isset($this->$name);
    }
    return isset($this->data->$name);
  }

  /**
   * Sets a property.
   *
   * You do not need to explicitly call this method. It is called by PHP when
   * you access the property normally.
   *
   * @param string $name The name of the property.
   * @param mixed $value The value of the property.
   * @return mixed The value of the property.
   */
  public function __set($name, $value) {
    // When providing defaults, a subclass may try to set values before the
    // constructor has run.
    if (!isset($this->data)) {
      $this->data = new EntityData();
    }
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    if ($name === 'guid') {
      return ($this->$name = isset($value) ? (int) $value : null);
    }
    if ($name === 'cdate' || $name === 'mdate') {
      return ($this->$name = floor(((float) $value) * 10000) / 10000);
    }
    if ($name === 'tags') {
      return ($this->$name = (array) $value);
    }
    return ($this->data->$name = $value);
  }

  /**
   * Unsets a property.
   *
   * You do not need to explicitly call this method. It is called by PHP when
   * you access the property normally.
   *
   * @param string $name The name of the property.
   */
  public function __unset($name) {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    if ($name === 'guid') {
      unset($this->$name);
      return;
    }
    if ($name === 'cdate') {
      $this->$name = null;
      return;
    }
    if ($name === 'mdate') {
      $this->$name = null;
      return;
    }
    if ($name === 'tags') {
      $this->$name = [];
      return;
    }
    unset($this->data->$name);
  }

  public function addTag() {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    $tagArray = func_get_args();
    if (is_array($tagArray[0])) {
      $tagArray = $tagArray[0];
    }
    if (empty($tagArray)) {
      return;
    }
    foreach ($tagArray as $tag) {
      $this->tags[] = $tag;
    }
    $this->tags = array_keys(array_flip($this->tags));
  }

  public function arraySearch($array, $strict = false) {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    if (!is_array($array)) {
      return false;
    }
    foreach ($array as $key => $curEntity) {
      if ($strict ? $this->equals($curEntity) : $this->is($curEntity)) {
        return $key;
      }
    }
    return false;
  }

  public function clearCache() {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }

    $this->data->putData(
      $this->data->getData(),
      $this->data->getSData(),
      $this->useSkipAc
    );
  }

  public function clientEnabledMethods() {
    return $this->clientEnabledMethods;
  }

  public function delete() {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    return Nymph::deleteEntity($this);
  }

  public function equals(&$object) {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    // If this object is hooked, get the real object.
    if (is_a($object, '\SciActive\HookOverride')) {
      $testObject = $object->_hookObject();
    } else {
      $testObject = $object;
    }
    if (!is_a($testObject, '\Nymph\Entity')) {
      return false;
    }
    if (isset($this->guid) || isset($testObject->guid)) {
      if ($this->guid !== $testObject->guid) {
        return false;
      }
    }
    if (get_class($testObject) !== get_class($this)) {
      return false;
    }
    if ($testObject->cdate !== $this->cdate) {
      return false;
    }
    if ($testObject->mdate !== $this->mdate) {
      return false;
    }
    $obData = $testObject->getData(true);
    $myData = $this->getData(true);
    return ($obData == $myData);
  }

  public function getData($includeSData = false) {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    if ($includeSData) {
      // Access all the serialized properties to initialize them.
      $sdata = $this->data->getSData();
      foreach ($sdata as $key => $value) {
        $unused = $this->data->$key;
      }
    }
    return $this->data->getData();
  }

  public function getSData() {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    return $this->data->getSData();
  }

  public function getOriginalAcValues() {
    return $this->originalAcValues;
  }

  public function getValidatable() {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    $sdata = $this->data->getSData();
    foreach ($sdata as $key => $value) {
      $unused = $this->data->$key;
    }
    // Access all the serialized properties to initialize them.
    $data = $this->data->getData(false);
    $data['guid'] = $this->guid;
    $data['cdate'] = $this->cdate;
    $data['mdate'] = $this->mdate;
    $data['tags'] = $this->tags;
    array_walk_recursive(
      $data,
      function (&$item) {
        if (is_a($item, '\SciActive\HookOverride')
          && is_callable([$item, '_hookObject'])
        ) {
          $item = $item->_hookObject();
        }
      }
    );
    return (object) $data;
  }

  public function getTags() {
    return $this->tags;
  }

  public function hasTag() {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    if (!is_array($this->tags)) {
      return false;
    }
    $tagArray = func_get_args();
    if (!$tagArray) {
      return false;
    }
    if (is_array($tagArray[0])) {
      $tagArray = $tagArray[0];
    }
    foreach ($tagArray as $tag) {
      if (!in_array($tag, $this->tags)) {
        return false;
      }
    }
    return true;
  }

  public function inArray($array, $strict = false) {
    return $this->arraySearch($array, $strict) !== false;
  }

  public function is(&$object) {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    // If this object is hooked, get the real object.
    if (is_a($object, '\SciActive\HookOverride')) {
      $testObject = $object->_hookObject();
    } else {
      $testObject = $object;
    }
    if (!is_a($testObject, '\Nymph\Entity')) {
      return false;
    }
    if (isset($this->guid) || isset($testObject->guid)) {
      return ($this->guid === $testObject->guid);
    } elseif (!is_callable([$testObject, 'getData'])) {
      return false;
    } else {
      $obData = $testObject->getData(true);
      $myData = $this->getData(true);
      return ($obData == $myData);
    }
  }

  public function jsonSerialize() {
    if ($this->isASleepingReference) {
      return $this->sleepingReference;
    }
    $object = (object) [];
    $object->guid = $this->guid;
    $object->cdate = $this->cdate;
    $object->mdate = $this->mdate;
    $object->tags = $this->tags;
    $object->data = [];
    foreach ($this->getData(true) as $key => $val) {
      if (!in_array($key, $this->privateData)) {
        $object->data[$key] = $val;
      }
    }
    $object->class = get_class($this);
    return $object;
  }

  public function jsonAcceptData($input, $allowConflict = false) {
    // TODO: Do this without causing everything to become unserialized.
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }

    // Accept the modified date.
    $mdate = key_exists('mdate', $input) ? $input['mdate'] : 0;
    $thismdate = isset($this->mdate) ? $this->mdate : 0;
    if (abs($mdate - $thismdate) >= 0.001 && !$allowConflict) {
      throw new Exceptions\EntityConflictException();
    }
    $this->mdate = $input['mdate'];

    // Accept the tags.
    $currentTags = $this->getTags();
    $protectedTags = array_intersect($this->protectedTags, $currentTags);
    $tags = array_diff($input['tags'], $this->protectedTags);

    if ($this->whitelistTags !== false) {
      $tags = array_intersect($tags, $this->whitelistTags);
    }

    $this->removeTag($currentTags);
    $this->addTag(array_keys(array_flip(array_merge($tags, $protectedTags))));

    // Accept the data.
    $data = $input['data'];
    foreach ($this->objectData as $name) {
      if (isset($data[$name]) && is_array($data[$name])) {
        $data[$name] = (object) $data[$name];
      }
    }

    $privateData = [];
    foreach ($this->privateData as $name) {
      if (isset($this->data->$name)) {
        $privateData[$name] = $this->data->$name;
      }
      if (key_exists($name, $data)) {
        unset($data[$name]);
      }
    }

    $protectedData = [];
    $protectedProps = $this->protectedData;
    if (class_exists('\Tilmeld\Tilmeld')
      && !\Tilmeld\Tilmeld::gatekeeper('tilmeld/admin')
    ) {
      $protectedProps[] = 'user';
      $protectedProps[] = 'group';
    }
    foreach ($protectedProps as $name) {
      if (isset($this->data->$name)) {
        $protectedData[$name] = $this->data->$name;
      }
      if (key_exists($name, $data)) {
        unset($data[$name]);
      }
    }

    $nonWhitelistData = [];
    if ($this->whitelistData !== false) {
      $nonWhitelistData = $this->getData(true);
      foreach ($this->whitelistData as $name) {
        unset($nonWhitelistData[$name]);
      }
      foreach ($data as $name => $val) {
        if (!in_array($name, $this->whitelistData)) {
          unset($data[$name]);
        }
      }
    }

    $newData = array_merge(
      $nonWhitelistData,
      $data,
      $protectedData,
      $privateData
    );

    $this->putData($newData);
  }

  public function jsonAcceptPatch($patch, $allowConflict = false) {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }

    // Accept the modified date.
    $mdate = key_exists('mdate', $patch) ? $patch['mdate'] : 0;
    $thismdate = isset($this->mdate) ? $this->mdate : 0;
    if (abs($mdate - $thismdate) >= 0.001 && !$allowConflict) {
      throw new Exceptions\EntityConflictException();
    }
    $this->mdate = $patch['mdate'];

    foreach ($this->objectData as $name) {
      if (isset($patch['set'][$name]) && is_array($patch['set'][$name])) {
        $patch['set'][$name] = (object) $patch['set'][$name];
      }
    }

    foreach ($patch['set'] as $name => $value) {
      if ((
          $this->whitelistData !== false
          && !in_array($name, $this->whitelistData)
        )
        || in_array($name, $this->protectedData)
        || in_array($name, $this->privateData)
      ) {
        continue;
      }
      $this->data->$name = $value;
    }

    foreach ($patch['unset'] as $name) {
      if ((
          $this->whitelistData !== false
          && !in_array($name, $this->whitelistData)
        )
        || in_array($name, $this->protectedData)
        || in_array($name, $this->privateData)
      ) {
        continue;
      }
      unset($this->data->$name);
    }

    foreach ($patch['addTags'] as $tag) {
      if ((
          $this->whitelistTags !== false
          && !in_array($tag, $this->whitelistTags)
        )
        || in_array($tag, $this->protectedTags)
      ) {
        continue;
      }
      $this->addTag($tag);
    }

    foreach ($patch['removeTags'] as $tag) {
      if ((
          $this->whitelistTags !== false
          && !in_array($tag, $this->whitelistTags)
        )
        || in_array($tag, $this->protectedTags)
      ) {
        continue;
      }
      $this->removeTag($tag);
    }
  }

  public function putData($data, $sdata = []) {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    $this->data->putData($data, $sdata, $this->useSkipAc);

    if (empty($this->originalAcValues)) {
      $this->originalAcValues['user'] = $this->data->user ?? null;
      $this->originalAcValues['group'] = $this->data->group ?? null;
      $this->originalAcValues['acUser'] = $this->data->acUser ?? null;
      $this->originalAcValues['acGroup'] = $this->data->acGroup ?? null;
      $this->originalAcValues['acOther'] = $this->data->acOther ?? null;
      $this->originalAcValues['acRead'] = $this->data->acRead ?? null;
      $this->originalAcValues['acWrite'] = $this->data->acWrite ?? null;
      $this->originalAcValues['acFull'] = $this->data->acFull ?? null;
    }
  }

  /**
   * Set up a sleeping reference.
   * @param array $reference The reference to use to wake.
   */
  public function referenceSleep($reference) {
    if (count($reference) !== 3
      || $reference[0] !== 'nymph_entity_reference'
      || !is_int($reference[1])
      || !is_string($reference[2])
    ) {
      throw new Exceptions\InvalidParametersException(
        'referenceSleep expects parameter 1 to be a valid Nymph entity '.
          'reference.'
      );
    }
    $thisClass = get_class($this);
    if ($reference[2] !== $thisClass) {
      throw new Exceptions\InvalidParametersException(
        "referenceSleep can only be called with an entity reference of the ".
          "same class. Given class: {$reference[2]}; this class: $thisClass."
      );
    }
    $this->isASleepingReference = true;
    $this->guid = $reference[1];
    $this->sleepingReference = $reference;
  }

  /**
   * Wake from a sleeping reference.
   *
   * @return bool True on success, false on failure.
   */
  private function referenceWake() {
    if (!$this->isASleepingReference) {
      return true;
    }
    if (!class_exists($this->sleepingReference[2])) {
      throw new Exceptions\EntityClassNotFoundException(
        "Tried to wake sleeping reference entity that refers to a class ".
          "that can't be found, {$this->sleepingReference[2]}."
      );
    }
    $entity = Nymph::getEntity(
      ['class' => $this->sleepingReference[2], 'skip_ac' => $this->useSkipAc],
      ['&', 'guid' => $this->sleepingReference[1]]
    );
    if (!isset($entity)) {
      return false;
    }
    $this->isASleepingReference = false;
    $this->sleepingReference = null;
    $this->guid = $entity->guid;
    $this->tags = $entity->tags;
    $this->cdate = $entity->cdate;
    $this->mdate = $entity->mdate;
    $this->putData($entity->getData(), $entity->getSData());
    return true;
  }

  public function refresh() {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    if (!isset($this->guid)) {
      return false;
    }
    $refresh = Nymph::getEntity(
      [
        'class' => get_class($this),
        'skip_cache' => true,
        'skip_ac' => $this->useSkipAc
      ],
      ['&', 'guid' => $this->guid]
    );
    if (!isset($refresh)) {
      return 0;
    }
    $this->tags = $refresh->tags;
    $this->cdate = $refresh->cdate;
    $this->mdate = $refresh->mdate;
    $this->putData($refresh->getData(), $refresh->getSData());
    return true;
  }

  public function removeTag() {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    $tagArray = func_get_args();
    if (is_array($tagArray[0])) {
      $tagArray = $tagArray[0];
    }
    foreach ($tagArray as $tag) {
      // Can't use array_search, because $tag may exist more than once.
      foreach ($this->tags as $curKey => $curTag) {
        if ($curTag === $tag) {
          unset($this->tags[$curKey]);
        }
      }
    }
    $this->tags = array_values($this->tags);
  }

  public function save() {
    if ($this->isASleepingReference) {
      $this->referenceWake();
    }
    return Nymph::saveEntity($this);
  }

  public function toReference() {
    if ($this->isASleepingReference) {
      return $this->sleepingReference;
    }
    if (!isset($this->guid)) {
      return $this;
    }
    return ['nymph_entity_reference', $this->guid, get_class($this)];
  }

  public function useSkipAc($useSkipAc) {
    $this->useSkipAc = (bool) $useSkipAc;
  }
}


text:
package org.jboss.modules;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLConnection;
import java.security.AccessController;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import org.jboss.modules._private.ModulesPrivateAccess;
import org.jboss.modules.filter.ClassFilter;
import org.jboss.modules.filter.ClassFilters;
import org.jboss.modules.filter.PathFilter;
import org.jboss.modules.filter.PathFilters;
import org.jboss.modules.log.ModuleLogger;
import org.jboss.modules.log.NoopModuleLogger;

import __redirected.__JAXPRedirected;
import org.jboss.modules.security.ModularPermissionFactory;

/**
 * A module is a unit of classes and other resources, along with the specification of what is imported and exported
 * by this module from and to other modules.  Modules are created by {@link ModuleLoader}s which build modules from
 * various configuration information and resource roots.
 *
 * @author David M. Lloyd
 * @author John Bailey
 * @author Flavia Rainone
 * @author Jason T. Greene
 * @author thomas.diesler@jboss.com
 *
 * @apiviz.landmark
*/
public final class Module {

    private static final AtomicReference<ModuleLoader> BOOT_MODULE_LOADER;

    static {
        log = NoopModuleLogger.getInstance();
        BOOT_MODULE_LOADER = new AtomicReference<ModuleLoader>();
        EMPTY_CLASS_FILTERS = new FastCopyHashSet<ClassFilter>(0);
        EMPTY_PATH_FILTERS = new FastCopyHashSet<PathFilter>(0);
        GET_DEPENDENCIES = new RuntimePermission("getDependencies");
        GET_CLASS_LOADER = new RuntimePermission("getClassLoader");
        GET_BOOT_MODULE_LOADER = new RuntimePermission("getBootModuleLoader");
        ACCESS_MODULE_LOGGER = new RuntimePermission("accessModuleLogger");
        ADD_CONTENT_HANDLER_FACTORY = new RuntimePermission("addContentHandlerFactory");
        ADD_URL_STREAM_HANDLER_FACTORY = new RuntimePermission("addURLStreamHandlerFactory");

        final String pkgsString = AccessController.doPrivileged(new PropertyReadAction("jboss.modules.system.pkgs"));
        final List<String> list = new ArrayList<String>();
        list.add("java.");
        list.add("sun.reflect.");
        list.add("__redirected.");
        if (pkgsString != null) {
            int i;
            int nc = -1;
            do {
                i = nc + 1;
                nc = pkgsString.indexOf(',', i);
                String part;
                if (nc == -1) {
                    part = pkgsString.substring(i).trim();
                } else {
                    part = pkgsString.substring(i, nc).trim();
                }
                if (part.length() > 0) {
                    list.add((part + ".").intern());
                }
            } while (nc != -1);
        }
        final String[] noStrings = new String[0];
        systemPackages = list.toArray(noStrings);
        final ListIterator<String> iterator = list.listIterator();
        // http://youtrack.jetbrains.net/issue/IDEA-72097
        //noinspection WhileLoopReplaceableByForEach
        while (iterator.hasNext()) {
            iterator.set(iterator.next().replace('.', '/'));
        }
        systemPaths = list.toArray(noStrings);

        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                try {
                    URL.setURLStreamHandlerFactory(ModularURLStreamHandlerFactory.INSTANCE);
                } catch (Throwable t) {
                    // todo log a warning or something
                }
                try {
                    URLConnection.setContentHandlerFactory(ModularContentHandlerFactory.INSTANCE);
                } catch (Throwable t) {
                    // todo log a warning or something
                }

                __JAXPRedirected.initAll();

                return null;
            }
        });
    }

    // static properties

    static final String[] systemPackages;
    static final String[] systemPaths;

    static final ModulesPrivateAccess PRIVATE_ACCESS = new ModulesPrivateAccess() {
        public ModuleClassLoader getClassLoaderOf(final Module module) {
            return module.getClassLoaderPrivate();
        }
    };

    /**
     * Private access for module internal code.  Throws {@link SecurityException} for user code.
     *
     * @throws SecurityException always
     */
    public static ModulesPrivateAccess getPrivateAccess() {
        if (CallerContext.getCallingClass() == ModularPermissionFactory.class) {
            return PRIVATE_ACCESS;
        }
        throw new SecurityException();
    }

    /**
     * The system-wide module logger, which may be changed via {@link #setModuleLogger(org.jboss.modules.log.ModuleLogger)}.
     */
    static volatile ModuleLogger log;

    private static final FastCopyHashSet<ClassFilter> EMPTY_CLASS_FILTERS;
    private static final FastCopyHashSet<PathFilter> EMPTY_PATH_FILTERS;

    // immutable properties

    /**
     * The identifier of this module.
     */
    private final ModuleIdentifier identifier;
    /**
     * The name of the main class, if any (may be {@code null}).
     */
    private final String mainClassName;
    /**
     * The module class loader for this module.
     */
    private final ModuleClassLoader moduleClassLoader;
    /**
     * The module loader which created this module.
     */
    private final ModuleLoader moduleLoader;
    /**
     * The fallback local loader, if any is defined.
     */
    private final LocalLoader fallbackLoader;
    /**
     * The properties map specified when this module was defined.
     */
    private final Map<String, String> properties;
    /**
     * The assigned permission collection.
     */
    private final PermissionCollection permissionCollection;

    // mutable properties

    /**
     * The linkage state.
     */
    private volatile Linkage linkage = Linkage.NONE;

    // private constants

    private static final RuntimePermission GET_DEPENDENCIES;
    private static final RuntimePermission GET_CLASS_LOADER;
    private static final RuntimePermission GET_BOOT_MODULE_LOADER;
    private static final RuntimePermission ACCESS_MODULE_LOGGER;
    private static final RuntimePermission ADD_CONTENT_HANDLER_FACTORY;
    private static final RuntimePermission ADD_URL_STREAM_HANDLER_FACTORY;

    private static final PermissionCollection NO_PERMISSIONS = noPermissions();

    /**
     * Construct a new instance from a module specification.
     *
     * @param spec the module specification
     * @param moduleLoader the module loader
     */
    Module(final ConcreteModuleSpec spec, final ModuleLoader moduleLoader) {
        this.moduleLoader = moduleLoader;

        // Initialize state from the spec.
        identifier = spec.getModuleIdentifier();
        mainClassName = spec.getMainClass();
        fallbackLoader = spec.getFallbackLoader();
        final PermissionCollection permissionCollection = spec.getPermissionCollection();
        this.permissionCollection = permissionCollection == null ? NO_PERMISSIONS : permissionCollection.isReadOnly() ? permissionCollection : copyPermissions(permissionCollection);
        //noinspection ThisEscapedInObjectConstruction
        final ModuleClassLoader.Configuration configuration = new ModuleClassLoader.Configuration(this, spec.getAssertionSetting(), spec.getResourceLoaders(), spec.getClassFileTransformer());
        final ModuleClassLoaderFactory factory = spec.getModuleClassLoaderFactory();
        ModuleClassLoader moduleClassLoader = null;
        if (factory != null) moduleClassLoader = factory.create(configuration);
        if (moduleClassLoader == null) moduleClassLoader = new ModuleClassLoader(configuration);
        this.moduleClassLoader = moduleClassLoader;
        final Map<String, String> properties = spec.getProperties();
        this.properties = properties.isEmpty() ? Collections.<String, String>emptyMap() : new LinkedHashMap<String, String>(properties);
    }

    private static PermissionCollection noPermissions() {
        final Permissions permissions = new Permissions();
        permissions.setReadOnly();
        return permissions;
    }

    private static PermissionCollection copyPermissions(PermissionCollection permissionCollection) {
        final Permissions permissions = new Permissions();
        final Enumeration<Permission> elements = permissionCollection.elements();
        while (elements.hasMoreElements()) {
            permissions.add(elements.nextElement());
        }
        permissions.setReadOnly();
        return permissions;
    }

    LocalLoader getFallbackLoader() {
        return fallbackLoader;
    }

    Dependency[] getDependenciesInternal() {
        return linkage.getDependencies();
    }

    DependencySpec[] getDependencySpecsInternal() {
        return linkage.getDependencySpecs();
    }

    ModuleClassLoader getClassLoaderPrivate() {
        return moduleClassLoader;
    }

    /**
     * Get the current dependencies of this module.
     *
     * @return the current dependencies of this module
     * @throws SecurityException if a security manager is enabled and the caller does not have the {@code getDependencies}
     * {@link RuntimePermission}
     */
    public DependencySpec[] getDependencies() throws SecurityException {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(GET_DEPENDENCIES);
        }
        return getDependencySpecsInternal().clone();
    }

    /**
     * Get an exported resource from a specific root in this module.
     *
     * @param rootPath the module root to search
     * @param resourcePath the path of the resource
     * @return the resource
     */
    public Resource getExportedResource(final String rootPath, final String resourcePath) {
        return moduleClassLoader.loadResourceLocal(rootPath, resourcePath);
    }

    /**
     * Run a module's main class, if any.
     *
     * @param args the arguments to pass
     * @throws NoSuchMethodException if there is no main method
     * @throws InvocationTargetException if the main method failed
     * @throws ClassNotFoundException if the main class is not found
     */
    public void run(final String[] args) throws NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
        try {
            if (mainClassName == null) {
                throw new NoSuchMethodException("No main class defined for " + this);
            }
            final ClassLoader oldClassLoader = SecurityActions.setContextClassLoader(moduleClassLoader);
            try {
                final Class<?> mainClass = Class.forName(mainClassName, false, moduleClassLoader);
                try {
                    Class.forName(mainClassName, true, moduleClassLoader);
                } catch (Throwable t) {
                    throw new InvocationTargetException(t, "Failed to initialize main class '" + mainClassName + "'");
                }
                final Method mainMethod = mainClass.getMethod("main", String[].class);
                final int modifiers = mainMethod.getModifiers();
                if (! Modifier.isStatic(modifiers)) {
                    throw new NoSuchMethodException("Main method is not static for " + this);
                }
                // ignore the return value
                mainMethod.invoke(null, new Object[] {args});
            } finally {
                SecurityActions.setContextClassLoader(oldClassLoader);
            }
        } catch (IllegalAccessException e) {
            // unexpected; should be public
            throw new IllegalAccessError(e.getMessage());
        }
    }

    /**
     * Get this module's identifier.
     *
     * @return the identifier
     */
    public ModuleIdentifier getIdentifier() {
        return identifier;
    }

    /**
     * Get the module loader which created this module.
     *
     * @return the module loader of this module
     */
    public ModuleLoader getModuleLoader() {
        return moduleLoader;
    }

    /**
     * Load a service loader from this module.
     *
     * @param serviceType the service type class
     * @param <S> the service type
     * @return the service loader
     */
    public <S> ServiceLoader<S> loadService(Class<S> serviceType) {
        return ServiceLoader.load(serviceType, moduleClassLoader);
    }

    /**
     * Load a service loader from this module, without looking at dependencies.
     *
     * @param serviceType the service type class
     * @param <S> the service type
     * @return the service loader
     */
    public <S> ServiceLoader<S> loadServiceDirectly(Class<S> serviceType) {
        return ServiceLoader.load(serviceType, new ClassLoader(null) {
            public Enumeration<URL> getResources(final String name) throws IOException {
                final Enumeration<Resource> resourceEnumeration = Collections.enumeration(getClassLoader().getLocalLoader().loadResourceLocal(name));
                return new Enumeration<URL>() {
                    public boolean hasMoreElements() {
                        return resourceEnumeration.hasMoreElements();
                    }

                    public URL nextElement() {
                        return resourceEnumeration.nextElement().getURL();
                    }
                };
            }
        });
    }

    /**
     * Load a service loader from a module in the caller's module loader. The caller's
     * module loader refers to the loader of the module of the class that calls this method.
     * Note that {@link #loadService(Class)} is more efficient since it does not need to crawl
     * the stack.
     *
     * @param <S> the the service type
     * @param identifier the module identifier containing the service loader
     * @param serviceType the service type class
     * @return the loaded service from the caller's module
     * @throws ModuleLoadException if the named module failed to load
     */
    public static <S> ServiceLoader<S> loadServiceFromCallerModuleLoader(ModuleIdentifier identifier, Class<S> serviceType) throws ModuleLoadException {
        return getCallerModuleLoader().loadModule(identifier).loadService(serviceType);
    }

    /**
     * Get the class loader for a module.  The class loader can be used to access non-exported classes and
     * resources of the module.
     *

     * If a security manager is present, then this method invokes the security manager's {@code checkPermission} method
     * with a RuntimePermission("getClassLoader") permission to verify access to the class loader. If
     * access is not granted, a {@code SecurityException} will be thrown.
     *
     * @return the module class loader
     */
    public ModuleClassLoader getClassLoader() {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(GET_CLASS_LOADER);
        }
        return moduleClassLoader;
    }

    /**
     * Get all the paths exported by this module.
     *
     * @return the paths that are exported by this module
     */
    public Set<String> getExportedPaths() {
        return Collections.unmodifiableSet(getPathsUnchecked().keySet());
    }

    /**
     * Get the module for a loaded class, or {@code null} if the class did not come from any module.
     *
     * @param clazz the class
     * @return the module it came from
     */
    public static Module forClass(Class<?> clazz) {
        final ClassLoader cl = clazz.getClassLoader();
        return forClassLoader(cl, false);
    }

    /**
     * Get the module for a class loader, or {@code null} if the class loader is not associated with any module.  If
     * the class loader is unknown, it is possible to check the parent class loader up the chain, and so on until a module is found.
     *
     * @param cl the class loader
     * @param search {@code true} to search up the delegation chain
     * @return the associated module
     */
    public static Module forClassLoader(ClassLoader cl, boolean search) {
        if (cl instanceof ModuleClassLoader) {
            return ((ModuleClassLoader) cl).getModule();
        } else if (search && cl != null) {
            return forClassLoader(cl.getParent(), true);
        } else {
            return null;
        }
    }

    /**
     * Gets the boot module loader. The boot module loader is the
     * initial loader that is established by the module framework. It typically
     * is based off of the environmental module path unless it is overridden by
     * specifying a different class name for the {@code boot.module.loader} system
     * property.
     *
     * @return the boot module loader
     */
    public static ModuleLoader getBootModuleLoader() {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(GET_BOOT_MODULE_LOADER);
        }
        ModuleLoader loader;
        while ((loader = BOOT_MODULE_LOADER.get()) == null) {
            loader = DefaultBootModuleLoaderHolder.INSTANCE;
            if (BOOT_MODULE_LOADER.compareAndSet(null, loader)) {
                break;
            }
            // get it again
        }
        return loader;
    }

    static void initBootModuleLoader(ModuleLoader loader) {
        BOOT_MODULE_LOADER.set(loader);
    }

    /**
     * Gets the current module loader. The current module loader is the
     * loader of the module from the calling class. Note that this method
     * must crawl the stack to determine this, so other mechanisms are more
     * efficient.
     *
     * @return the current module loader, or {@code null} if this method is called outside of a module
     */
    public static ModuleLoader getCallerModuleLoader() {
        Module callerModule = getCallerModule();
        return callerModule == null ? null : callerModule.getModuleLoader();
    }

    /**
     * Get the current thread's context module loader.  This loader is the one which defined the module
     * whose class loader is, or is a parent of, the thread's current context class loader.  If there is none,
     * then {@code null} is returned.
     *
     * @return the module loader, or {@code null} if none is set
     */
    public static ModuleLoader getContextModuleLoader() {
        return Module.forClassLoader(Thread.currentThread().getContextClassLoader(), true).getModuleLoader();
    }

    /**
     * Get a module from the current module loader. Note that this must crawl the
     * stack to determine this, so other mechanisms are more efficient.
     * @see #getCallerModuleLoader()
     *
     * @param identifier the module identifier
     * @return the module
     * @throws ModuleLoadException if the module could not be loaded
     */
    public static Module getModuleFromCallerModuleLoader(final ModuleIdentifier identifier) throws ModuleLoadException {
        return getCallerModuleLoader().loadModule(identifier);
    }

    /**
     * Get the caller's module. The caller's module is the module containing the method that calls this
     * method. Note that this method crawls the stack so other ways of obtaining the
     * module are more efficient.
     *
     * @return the current module
     */
    public static Module getCallerModule() {
        return forClass(CallerContext.getCallingClass());
    }

    /**
     * Get the module with the given identifier from the module loader used by this module.
     *
     * @param identifier the module identifier
     * @return the module
     * @throws ModuleLoadException if an error occurs
     */
    public Module getModule(final ModuleIdentifier identifier) throws ModuleLoadException {
        return moduleLoader.loadModule(identifier);
    }

    /**
     * Load a class from a module in the system module loader.
     *
     * @see #getBootModuleLoader()
     *
     * @param moduleIdentifier the identifier of the module from which the class
     *        should be loaded
     * @param className the class name to load
     * @return the class
     * @throws ModuleLoadException if the module could not be loaded
     * @throws ClassNotFoundException if the class could not be loaded
     */
    public static Class<?> loadClassFromBootModuleLoader(final ModuleIdentifier moduleIdentifier, final String className)
            throws ModuleLoadException, ClassNotFoundException {
        return Class.forName(className, true, getBootModuleLoader().loadModule(moduleIdentifier).getClassLoader());
    }

    /**
     * Load a class from a module in the caller's module loader.
     *
     * @see #getCallerModuleLoader()
     *
     * @param moduleIdentifier the identifier of the module from which the class
     *        should be loaded
     * @param className the class name to load
     * @return the class
     * @throws ModuleLoadException if the module could not be loaded
     * @throws ClassNotFoundException if the class could not be loaded
     */
    public static Class<?> loadClassFromCallerModuleLoader(final ModuleIdentifier moduleIdentifier, final String className)
            throws ModuleLoadException, ClassNotFoundException {
        return Class.forName(className, true, getModuleFromCallerModuleLoader(moduleIdentifier).getClassLoader());
    }

    /**
     * Load a class from a local loader.
     *
     * @param className the class name
     * @param resolve {@code true} to resolve the class after definition
     * @return the class
     */
    Class<?> loadModuleClass(final String className, final boolean resolve) throws ClassNotFoundException {
        for (String s : systemPackages) {
            if (className.startsWith(s)) {
                return moduleClassLoader.loadClass(className, resolve);
            }
        }
        final String path = pathOfClass(className);
        final Map<String, List<LocalLoader>> paths = getPathsUnchecked();
        final List<LocalLoader> loaders = paths.get(path);
        if (loaders != null) {
            Class<?> clazz;
            for (LocalLoader loader : loaders) {
                clazz = loader.loadClassLocal(className, resolve);
                if (clazz != null) {
                    return clazz;
                }
            }
        }
        final LocalLoader fallbackLoader = this.fallbackLoader;
        if (fallbackLoader != null) {
            return fallbackLoader.loadClassLocal(className, resolve);
        }
        return null;
    }

    /**
     * Load a resource from a local loader.
     *
     * @param name the resource name
     * @return the resource URL, or {@code null} if not found
     */
    URL getResource(final String name) {
        final String canonPath = PathUtils.canonicalize(name);
        for (String s : Module.systemPaths) {
            if (canonPath.startsWith(s)) {
                return moduleClassLoader.getResource(canonPath);
            }
        }
        log.trace("Attempting to find resource %s in %s", canonPath, this);
        final String path = pathOf(canonPath);
        final Map<String, List<LocalLoader>> paths = getPathsUnchecked();
        final List<LocalLoader> loaders = paths.get(path);
        if (loaders != null) {
            for (LocalLoader loader : loaders) {
                final List<Resource> resourceList = loader.loadResourceLocal(canonPath);
                for (Resource resource : resourceList) {
                    return resource.getURL();
                }
            }
        }
        final LocalLoader fallbackLoader = this.fallbackLoader;
        if (fallbackLoader != null) {
            final List<Resource> resourceList = fallbackLoader.loadResourceLocal(canonPath);
            for (Resource resource : resourceList) {
                return resource.getURL();
            }
        }
        return null;
    }

    /**
     * Load a resource from a local loader.
     *
     * @param name the resource name
     * @return the resource stream, or {@code null} if not found
     */
    InputStream getResourceAsStream(final String name) throws IOException {
        final String canonPath = PathUtils.canonicalize(name);
        for (String s : Module.systemPaths) {
            if (canonPath.startsWith(s)) {
                return moduleClassLoader.getResourceAsStream(canonPath);
            }
        }
        log.trace("Attempting to find resource %s in %s", canonPath, this);
        final String path = pathOf(canonPath);
        final Map<String, List<LocalLoader>> paths = getPathsUnchecked();
        final List<LocalLoader> loaders = paths.get(path);
        if (loaders != null) {
            for (LocalLoader loader : loaders) {
                final List<Resource> resourceList = loader.loadResourceLocal(canonPath);
                for (Resource resource : resourceList) {
                    return resource.openStream();
                }
            }
        }
        final LocalLoader fallbackLoader = this.fallbackLoader;
        if (fallbackLoader != null) {
            final List<Resource> resourceList = fallbackLoader.loadResourceLocal(canonPath);
            for (Resource resource : resourceList) {
                return resource.openStream();
            }
        }
        return null;
    }

    /**
     * Load all resources of a given name from a local loader.
     *
     * @param name the resource name
     * @return the enumeration of all the matching resource URLs (may be empty)
     */
    Enumeration<URL> getResources(final String name) {
        final String canonPath = PathUtils.canonicalize(PathUtils.relativize(name));
        for (String s : Module.systemPaths) {
            if (canonPath.startsWith(s)) {
                try {
                    return moduleClassLoader.getResources(canonPath);
                } catch (IOException e) {
                    return ConcurrentClassLoader.EMPTY_ENUMERATION;
                }
            }
        }
        log.trace("Attempting to find all resources %s in %s", canonPath, this);
        final String path = pathOf(canonPath);
        final Map<String, List<LocalLoader>> paths = getPathsUnchecked();
        final List<LocalLoader> loaders = paths.get(path);

        final List<URL> list = new ArrayList<URL>();
        if (loaders != null) {
            for (LocalLoader loader : loaders) {
                final List<Resource> resourceList = loader.loadResourceLocal(canonPath);
                for (Resource resource : resourceList) {
                    list.add(resource.getURL());
                }
            }
        }
        final LocalLoader fallbackLoader = this.fallbackLoader;
        if (fallbackLoader != null) {
            final List<Resource> resourceList = fallbackLoader.loadResourceLocal(canonPath);
            for (Resource resource : resourceList) {
                list.add(resource.getURL());
            }
        }

        return list.size() == 0 ? ConcurrentClassLoader.EMPTY_ENUMERATION : Collections.enumeration(list);
    }

    /**
     * Get an exported resource URL.
     *
     * @param name the resource name
     * @return the resource, or {@code null} if it was not found
     */
    public URL getExportedResource(final String name) {
        return getResource(name);
    }

    /**
     * Get all exported resource URLs for a resource name.
     *
     * @param name the resource name
     * @return the resource URLs
     */
    public Enumeration<URL> getExportedResources(final String name) {
        return getResources(name);
    }

    /**
     * Enumerate all the imported resources in this module, subject to a path filter.  The filter applies to
     * the containing path of each resource.
     *
     * @param filter the filter to apply to the search
     * @return the resource iterator (possibly empty)
     * @throws ModuleLoadException if linking a dependency module fails for some reason
     */
    public Iterator<Resource> iterateResources(final PathFilter filter) throws ModuleLoadException {
        final Map<String, List<LocalLoader>> paths = getPaths();
        final Iterator<Map.Entry<String, List<LocalLoader>>> iterator = paths.entrySet().iterator();
        return new Iterator<Resource>() {

            private String path;
            private Iterator<Resource> resourceIterator;
            private Iterator<LocalLoader> loaderIterator;
            private Resource next;

            public boolean hasNext() {
                while (next == null) {
                    if (resourceIterator != null) {
                        assert path != null;
                        if (resourceIterator.hasNext()) {
                            next = resourceIterator.next();
                            return true;
                        }
                        resourceIterator = null;
                    }
                    if (loaderIterator != null) {
                        assert path != null;
                        if (loaderIterator.hasNext()) {
                            final LocalLoader loader = loaderIterator.next();
                            if (loader instanceof IterableLocalLoader) {
                                resourceIterator = ((IterableLocalLoader)loader).iterateResources(path, false);
                                continue;
                            }
                        }
                        loaderIterator = null;
                    }
                    if (! iterator.hasNext()) {
                        return false;
                    }
                    final Map.Entry<String, List<LocalLoader>> entry = iterator.next();
                    path = entry.getKey();
                    if (filter.accept(path)) {
                        loaderIterator = entry.getValue().iterator();
                    }
                }
                return true;
            }

            public Resource next() {
                if (! hasNext()) throw new NoSuchElementException();
                try {
                    return next;
                } finally {
                    next = null;
                }
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    /**
     * Enumerate all imported resources in this module which match the given glob expression.  The glob applies to
     * the whole resource name.
     *
     * @param glob the glob to apply
     * @return the iterator
     * @throws ModuleLoadException if linking a dependency module fails for some reason
     */
    public Iterator<Resource> globResources(final String glob) throws ModuleLoadException {
        String safeGlob = PathUtils.canonicalize(PathUtils.relativize(glob));
        final int i = safeGlob.lastIndexOf('/');
        if (i == -1) {
            return PathFilters.filtered(PathFilters.match(glob), iterateResources(PathFilters.acceptAll()));
        } else {
            return PathFilters.filtered(PathFilters.match(glob.substring(i + 1)), iterateResources(PathFilters.match(glob.substring(0, i))));
        }
    }

    /**
     * Get the (unmodifiable) set of paths which are imported into this module class loader, including local paths.  The
     * set will include all paths defined by the module's resource loaders, minus any paths excluded by filters.  The
     * set will generally always contain an empty entry ("").  The set is unordered and unsorted, and is iterable in
     * O(n) time and accessible in O(1) time.
     *
     * @return the set of paths
     * @throws ModuleLoadException if the module was previously unlinked, and there was an exception while linking
     */
    public Set<String> getImportedPaths() throws ModuleLoadException {
        return Collections.unmodifiableSet(getPaths().keySet());
    }

    /**
     * Get the path name of a class.
     *
     * @param className the binary name of the class
     * @return the parent path
     */
    static String pathOfClass(final String className) {
        final String resourceName = className.replace('.', '/');
        final String path;
        final int idx = resourceName.lastIndexOf('/');
        if (idx > -1) {
            path = resourceName.substring(0, idx);
        } else {
            path = "";
        }
        return path;
    }

    /**
     * Get the path name of a resource.
     *
     * @param resourceName the resource name
     * @return the parent path
     */
    static String pathOf(final String resourceName) {
        final String path;
        if (resourceName.indexOf('/') == 0) {
            return pathOf(resourceName.substring(1));
        }
        final int idx = resourceName.lastIndexOf('/');
        if (idx > -1) {
            path = resourceName.substring(0, idx);
        } else {
            path = "";
        }
        return path;
    }

    /**
     * Get the file name of a class.
     *
     * @param className the class name
     * @return the name of the corresponding class file
     */
    static String fileNameOfClass(final String className) {
        return className.replace('.', '/') + ".class";
    }

    /**
     * Get the property with the given name, or {@code null} if none was defined.
     *
     * @param name the property name
     * @return the property value
     */
    public String getProperty(String name) {
        return properties.get(name);
    }

    /**
     * Get the property with the given name, or a default value if none was defined.
     *
     * @param name the property name
     * @param defaultVal the default value
     * @return the property value
     */
    public String getProperty(String name, String defaultVal) {
        return properties.containsKey(name) ? properties.get(name) : defaultVal;
    }

    /**
     * Get a copy of the list of property names.
     *
     * @return the property names list
     */
    public List<String> getPropertyNames() {
        return new ArrayList<String>(properties.keySet());
    }

    /**
     * Get the string representation of this module.
     *
     * @return the string representation
     */
    @Override
    public String toString() {
        return "Module \"" + identifier + "\"" + " from " + moduleLoader.toString();
    }

    /**
     * Get the logger used by the module system.
     *

     * If a security manager is present, then this method invokes the security manager's {@code checkPermission} method
     * with a RuntimePermission("accessModuleLogger") permission to verify access to the module logger. If
     * access is not granted, a {@code SecurityException} will be thrown.
     *
     * @return the module logger
     */
    public static ModuleLogger getModuleLogger() {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(ACCESS_MODULE_LOGGER);
        }
        return log;
    }

    /**
     * Change the logger used by the module system.
     *

     * If a security manager is present, then this method invokes the security manager's {@code checkPermission} method
     * with a RuntimePermission("accessModuleLogger") permission to verify access to the module logger. If
     * access is not granted, a {@code SecurityException} will be thrown.
     *
     * @param logger the new logger, must not be {@code null}
     */
    public static void setModuleLogger(final ModuleLogger logger) {
        if (logger == null) {
            throw new IllegalArgumentException("logger is null");
        }
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(ACCESS_MODULE_LOGGER);
        }
        logger.greeting();
        log = logger;
    }

    /**
     * Return the start time in millis when Module.class was loaded.
     *
     * @return start time of Module.class load
     */
    public static long getStartTime() {
        return StartTimeHolder.START_TIME;
    }

    /**
     * Register an additional module which contains content handlers.
     *

     * If a security manager is present, then this method invokes the security manager's {@code checkPermission} method
     * with a RuntimePermission("addContentHandlerFactory") permission to verify access. If
     * access is not granted, a {@code SecurityException} will be thrown.
     *
     * @param module the module to add
     */
    public static void registerContentHandlerFactoryModule(Module module) {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(ADD_CONTENT_HANDLER_FACTORY);
        }
        ModularContentHandlerFactory.addHandlerModule(module);
    }

    /**
     * Register an additional module which contains URL handlers.
     *

     * If a security manager is present, then this method invokes the security manager's {@code checkPermission} method
     * with a RuntimePermission("addURLStreamHandlerFactory") permission to verify access. If
     * access is not granted, a {@code SecurityException} will be thrown.
     *
     * @param module the module to add
     */
    public static void registerURLStreamHandlerFactoryModule(Module module) {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(ADD_URL_STREAM_HANDLER_FACTORY);
        }
        ModularURLStreamHandlerFactory.addHandlerModule(module);
    }

    /**
     * Get the platform identifier.  This is the string that uniquely identifies the hardware and OS combination for
     * the current running system.
     *
     * @return the platform identifier
     */
    public static String getPlatformIdentifier() {
        return NativeLibraryResourceLoader.getArchName();
    }

    /**
     * Get the module's configured permission collection.
     *
     * @return the module permission collection
     */
    public PermissionCollection getPermissionCollection() {
        return permissionCollection;
    }

    // Linking and resolution

    static final class Visited {
        private final Module module;
        private final FastCopyHashSet<PathFilter> filters;
        private final FastCopyHashSet<ClassFilter> classFilters;
        private final FastCopyHashSet<PathFilter> resourceFilters;
        private final int hashCode;

        Visited(final Module module, final FastCopyHashSet<PathFilter> filters, final FastCopyHashSet<ClassFilter> classFilters, final FastCopyHashSet<PathFilter> resourceFilters) {
            this.module = module;
            this.filters = filters;
            this.classFilters = classFilters;
            this.resourceFilters = resourceFilters;
            hashCode = ((resourceFilters.hashCode() * 13 + classFilters.hashCode()) * 13 + filters.hashCode()) * 13 + module.hashCode();
        }

        public int hashCode() {
            return hashCode;
        }

        public boolean equals(Object other) {
            return other instanceof Visited && equals((Visited)other);
        }

        public boolean equals(Visited other) {
            return this == other || other != null && module == other.module && filters.equals(other.filters) && classFilters.equals(other.classFilters) && resourceFilters.equals(other.resourceFilters);
        }
    }

    private long addPaths(Dependency[] dependencies, Map<String, List<LocalLoader>> map, FastCopyHashSet<PathFilter> filterStack, FastCopyHashSet<ClassFilter> classFilterStack, final FastCopyHashSet<PathFilter> resourceFilterStack, Set<Visited> visited) throws ModuleLoadException {
        long subtract = 0L;
        moduleLoader.incScanCount();
        for (Dependency dependency : dependencies) {
            if (dependency instanceof ModuleDependency) {
                final ModuleDependency moduleDependency = (ModuleDependency) dependency;
                final ModuleLoader moduleLoader = moduleDependency.getModuleLoader();
                final ModuleIdentifier id = moduleDependency.getIdentifier();
                final Module module;

                try {
                    long pauseStart = Metrics.getCurrentCPUTime();
                    try {
                        module = moduleLoader.preloadModule(id);
                    } finally {
                        subtract += Metrics.getCurrentCPUTime() - pauseStart;
                    }
                } catch (ModuleLoadException ex) {
                    if (moduleDependency.isOptional()) {
                        continue;
                    } else {
                        log.trace("Module %s, dependency %s preload failed: %s", getIdentifier(), moduleDependency.getIdentifier(), ex);
                        throw ex;
                    }
                }
                if (module == null) {
                    if (!moduleDependency.isOptional()) {
                        throw new ModuleNotFoundException(id.toString());
                    }
                    continue;
                }

                final PathFilter importFilter = dependency.getImportFilter();
                final FastCopyHashSet<PathFilter> nestedFilters;
                final FastCopyHashSet<ClassFilter> nestedClassFilters;
                final FastCopyHashSet<PathFilter> nestedResourceFilters;
                if (filterStack.contains(importFilter)) {
                    nestedFilters = filterStack;
                } else {
                    nestedFilters = filterStack.clone();
                    nestedFilters.add(importFilter);
                }
                final ClassFilter classImportFilter = dependency.getClassImportFilter();
                if (classImportFilter == ClassFilters.acceptAll() || classFilterStack.contains(classImportFilter)) {
                    nestedClassFilters = classFilterStack;
                } else {
                    nestedClassFilters = classFilterStack.clone();
                    if (classImportFilter != ClassFilters.acceptAll()) nestedClassFilters.add(classImportFilter);
                }
                final PathFilter resourceImportFilter = dependency.getResourceImportFilter();
                if (resourceImportFilter == PathFilters.acceptAll() || resourceFilterStack.contains(resourceImportFilter)) {
                    nestedResourceFilters = resourceFilterStack;
                } else {
                    nestedResourceFilters = resourceFilterStack.clone();
                    if (resourceImportFilter != PathFilters.acceptAll()) nestedResourceFilters.add(resourceImportFilter);
                }
                subtract += module.addExportedPaths(module.getDependenciesInternal(), map, nestedFilters, nestedClassFilters, nestedResourceFilters, visited);
            } else if (dependency instanceof ModuleClassLoaderDependency) {
                final ModuleClassLoaderDependency classLoaderDependency = (ModuleClassLoaderDependency) dependency;
                LocalLoader localLoader = classLoaderDependency.getLocalLoader();
                for (Object filter : classFilterStack.getRawArray()) {
                    if (filter != null && filter != ClassFilters.acceptAll()) {
                        localLoader = createClassFilteredLocalLoader((ClassFilter) filter, localLoader);
                    }
                }
                for (Object filter : resourceFilterStack.getRawArray()) {
                    if (filter != null && filter != PathFilters.acceptAll()) {
                        localLoader = createPathFilteredLocalLoader((PathFilter) filter, localLoader);
                    }
                }
                ClassFilter classFilter = classLoaderDependency.getClassImportFilter();
                if (classFilter != ClassFilters.acceptAll()) {
                    localLoader = createClassFilteredLocalLoader(classFilter, localLoader);
                }
                PathFilter resourceFilter = classLoaderDependency.getResourceImportFilter();
                if (resourceFilter != PathFilters.acceptAll()) {
                    localLoader = createPathFilteredLocalLoader(resourceFilter, localLoader);
                }
                final PathFilter importFilter = classLoaderDependency.getImportFilter();
                final Set<String> paths = classLoaderDependency.getPaths();
                for (String path : paths) {
                    if (importFilter.accept(path)) {
                        List<LocalLoader> list = map.get(path);
                        if (list == null) {
                            map.put(path, list = new ArrayList<LocalLoader>());
                            list.add(localLoader);
                        } else if (! list.contains(localLoader)) {
                            list.add(localLoader);
                        }
                    }
                }
            } else if (dependency instanceof LocalDependency) {
                final LocalDependency localDependency = (LocalDependency) dependency;
                LocalLoader localLoader = localDependency.getLocalLoader();
                for (Object filter : classFilterStack.getRawArray()) {
                    if (filter != null && filter != ClassFilters.acceptAll()) {
                        localLoader = createClassFilteredLocalLoader((ClassFilter) filter, localLoader);
                    }
                }
                for (Object filter : resourceFilterStack.getRawArray()) {
                    if (filter != null && filter != PathFilters.acceptAll()) {
                        localLoader = createPathFilteredLocalLoader((PathFilter) filter, localLoader);
                    }
                }
                final ClassFilter classFilter = localDependency.getClassImportFilter();
                if (classFilter != ClassFilters.acceptAll()) {
                    localLoader = createClassFilteredLocalLoader(classFilter, localLoader);
                }
                final PathFilter resourceFilter = localDependency.getResourceImportFilter();
                if (resourceFilter != PathFilters.acceptAll()) {
                    localLoader = createPathFilteredLocalLoader(resourceFilter, localLoader);
                }
                final PathFilter importFilter = localDependency.getImportFilter();
                final Set<String> paths = localDependency.getPaths();
                for (String path : paths) {
                    if (importFilter.accept(path)) {
                        List<LocalLoader> list = map.get(path);
                        if (list == null) {
                            map.put(path, list = new ArrayList<LocalLoader>());
                            list.add(localLoader);
                        } else if (! list.contains(localLoader)) {
                            list.add(localLoader);
                        }
                    }
                }
            }
            // else unknown dep type so just skip
        }
        return subtract;
    }

    private LocalLoader createPathFilteredLocalLoader(PathFilter filter, LocalLoader localLoader) {
        if (localLoader instanceof IterableLocalLoader)
            return LocalLoaders.createIterablePathFilteredLocalLoader(filter, (IterableLocalLoader) localLoader);
        else
            return LocalLoaders.createPathFilteredLocalLoader(filter, localLoader);
    }

    private LocalLoader createClassFilteredLocalLoader(ClassFilter filter, LocalLoader localLoader) {
        if (localLoader instanceof IterableLocalLoader)
            return LocalLoaders.createIterableClassFilteredLocalLoader(filter, (IterableLocalLoader) localLoader);
        else
            return LocalLoaders.createClassFilteredLocalLoader(filter, localLoader);
    }

    private long addExportedPaths(Dependency[] dependencies, Map<String, List<LocalLoader>> map, FastCopyHashSet<PathFilter> filterStack, FastCopyHashSet<ClassFilter> classFilterStack, final FastCopyHashSet<PathFilter> resourceFilterStack, Set<Visited> visited) throws ModuleLoadException {
        if (!visited.add(new Visited(this, filterStack, classFilterStack, resourceFilterStack))) {
            return 0L;
        }
        long subtract = 0L;
        moduleLoader.incScanCount();
        for (Dependency dependency : dependencies) {
            final PathFilter exportFilter = dependency.getExportFilter();
            // skip non-exported dependencies altogether
            if (exportFilter != PathFilters.rejectAll()) {
                if (dependency instanceof ModuleDependency) {
                    final ModuleDependency moduleDependency = (ModuleDependency) dependency;
                    final ModuleLoader moduleLoader = moduleDependency.getModuleLoader();
                    final ModuleIdentifier id = moduleDependency.getIdentifier();
                    final Module module;

                    try {
                        long pauseStart = Metrics.getCurrentCPUTime();
                        try {
                            module = moduleLoader.preloadModule(id);
                        } finally {
                            subtract += Metrics.getCurrentCPUTime() - pauseStart;
                        }
                    } catch (ModuleLoadException ex) {
                        if (moduleDependency.isOptional()) {
                            continue;
                        } else {
                            log.trace("Module %s, dependency %s preload failed: %s", getIdentifier(), moduleDependency.getIdentifier(), ex);
                            throw ex;
                        }
                    }
                    if (module == null) {
                        if (!moduleDependency.isOptional()) {
                            throw new ModuleNotFoundException(id.toString());
                        }
                        continue;
                    }

                    final PathFilter importFilter = dependency.getImportFilter();
                    final FastCopyHashSet<PathFilter> nestedFilters;
                    final FastCopyHashSet<ClassFilter> nestedClassFilters;
                    final FastCopyHashSet<PathFilter> nestedResourceFilters;
                    if (filterStack.contains(importFilter) && filterStack.contains(exportFilter)) {
                        nestedFilters = filterStack;
                    } else {
                        nestedFilters = filterStack.clone();
                        nestedFilters.add(importFilter);
                        nestedFilters.add(exportFilter);
                    }
                    final ClassFilter classImportFilter = dependency.getClassImportFilter();
                    final ClassFilter classExportFilter = dependency.getClassExportFilter();
                    if ((classImportFilter == ClassFilters.acceptAll() || classFilterStack.contains(classImportFilter)) && (classExportFilter == ClassFilters.acceptAll() || classFilterStack.contains(classExportFilter))) {
                        nestedClassFilters = classFilterStack;
                    } else {
                        nestedClassFilters = classFilterStack.clone();
                        if (classImportFilter != ClassFilters.acceptAll()) nestedClassFilters.add(classImportFilter);
                        if (classExportFilter != ClassFilters.acceptAll()) nestedClassFilters.add(classExportFilter);
                    }
                    final PathFilter resourceImportFilter = dependency.getResourceImportFilter();
                    final PathFilter resourceExportFilter = dependency.getResourceExportFilter();
                    if ((resourceImportFilter == PathFilters.acceptAll() || resourceFilterStack.contains(resourceImportFilter)) && (resourceExportFilter == PathFilters.acceptAll() || resourceFilterStack.contains(resourceExportFilter))) {
                        nestedResourceFilters = resourceFilterStack;
                    } else {
                        nestedResourceFilters = resourceFilterStack.clone();
                        if (resourceImportFilter != PathFilters.acceptAll()) nestedResourceFilters.add(resourceImportFilter);
                        if (resourceExportFilter != PathFilters.acceptAll()) nestedResourceFilters.add(resourceExportFilter);
                    }
                    subtract += module.addExportedPaths(module.getDependenciesInternal(), map, nestedFilters, nestedClassFilters, nestedResourceFilters, visited);
                } else if (dependency instanceof ModuleClassLoaderDependency) {
                    final ModuleClassLoaderDependency classLoaderDependency = (ModuleClassLoaderDependency) dependency;
                    LocalLoader localLoader = classLoaderDependency.getLocalLoader();
                    for (Object filter : classFilterStack.getRawArray()) {
                        if (filter != null && filter != ClassFilters.acceptAll()) {
                            localLoader = createClassFilteredLocalLoader((ClassFilter) filter, localLoader);
                        }
                    }
                    for (Object filter : resourceFilterStack.getRawArray()) {
                        if (filter != null && filter != PathFilters.acceptAll()) {
                            localLoader = createPathFilteredLocalLoader((PathFilter) filter, localLoader);
                        }
                    }
                    ClassFilter classImportFilter = classLoaderDependency.getClassImportFilter();
                    if (classImportFilter != ClassFilters.acceptAll()) {
                        localLoader = createClassFilteredLocalLoader(classImportFilter, localLoader);
                    }
                    ClassFilter classExportFilter = classLoaderDependency.getClassExportFilter();
                    if (classExportFilter != ClassFilters.acceptAll()) {
                        localLoader = createClassFilteredLocalLoader(classExportFilter, localLoader);
                    }
                    PathFilter resourceImportFilter = classLoaderDependency.getResourceImportFilter();
                    if (resourceImportFilter != PathFilters.acceptAll()) {
                        localLoader = createPathFilteredLocalLoader(resourceImportFilter, localLoader);
                    }
                    PathFilter resourceExportFilter = classLoaderDependency.getResourceExportFilter();
                    if (resourceExportFilter != PathFilters.acceptAll()) {
                        localLoader = createPathFilteredLocalLoader(resourceExportFilter, localLoader);
                    }
                    final PathFilter importFilter = classLoaderDependency.getImportFilter();
                    final Set<String> paths = classLoaderDependency.getPaths();
                    for (String path : paths) {
                        boolean accept = ! "_private".equals(path);
                        if (accept) for (Object filter : filterStack.getRawArray()) {
                            if (filter != null && ! ((PathFilter)filter).accept(path)) {
                                accept = false; break;
                            }
                        }
                        if (accept && importFilter.accept(path) && exportFilter.accept(path)) {
                            List<LocalLoader> list = map.get(path);
                            if (list == null) {
                                map.put(path, list = new ArrayList<LocalLoader>(1));
                                list.add(localLoader);
                            } else if (! list.contains(localLoader)) {
                                list.add(localLoader);
                            }
                        }
                    }
                } else if (dependency instanceof LocalDependency) {
                    final LocalDependency localDependency = (LocalDependency) dependency;
                    LocalLoader localLoader = localDependency.getLocalLoader();
                    for (Object filter : classFilterStack.getRawArray()) {
                        if (filter != null && filter != ClassFilters.acceptAll()) {
                            localLoader = createClassFilteredLocalLoader((ClassFilter) filter, localLoader);
                        }
                    }
                    for (Object filter : resourceFilterStack.getRawArray()) {
                        if (filter != null && filter != PathFilters.acceptAll()) {
                            localLoader = createPathFilteredLocalLoader((PathFilter) filter, localLoader);
                        }
                    }
                    ClassFilter classFilter = localDependency.getClassExportFilter();
                    if (classFilter != ClassFilters.acceptAll()) {
                        localLoader = createClassFilteredLocalLoader(classFilter, localLoader);
                    }
                    classFilter = localDependency.getClassImportFilter();
                    if (classFilter != ClassFilters.acceptAll()) {
                        localLoader = createClassFilteredLocalLoader(classFilter, localLoader);
                    }
                    PathFilter resourceFilter = localDependency.getResourceExportFilter();
                    if (resourceFilter != PathFilters.acceptAll()) {
                        localLoader = createPathFilteredLocalLoader(resourceFilter, localLoader);
                    }
                    resourceFilter = localDependency.getResourceImportFilter();
                    if (resourceFilter != PathFilters.acceptAll()) {
                        localLoader = createPathFilteredLocalLoader(resourceFilter, localLoader);
                    }
                    final Set<String> paths = localDependency.getPaths();
                    for (String path : paths) {
                        boolean accept = true;
                        for (Object filter : filterStack.getRawArray()) {
                            if (filter != null && ! ((PathFilter)filter).accept(path)) {
                                accept = false; break;
                            }
                        }
                        if (accept && localDependency.getImportFilter().accept(path) && localDependency.getExportFilter().accept(path)) {
                            List<LocalLoader> list = map.get(path);
                            if (list == null) {
                                map.put(path, list = new ArrayList<LocalLoader>(1));
                                list.add(localLoader);
                            } else if (! list.contains(localLoader)) {
                                list.add(localLoader);
                            }
                        }
                    }
                }
                // else unknown dep type so just skip
            }
        }
        return subtract;
    }

    Map<String, List<LocalLoader>> getPaths() throws ModuleLoadException {
        Linkage oldLinkage = this.linkage;
        Linkage linkage;
        Linkage.State state = oldLinkage.getState();
        if (state == Linkage.State.LINKED) {
            return oldLinkage.getPaths();
        }
        // slow path loop
        boolean intr = false;
        try {
            for (;;) {
                synchronized (this) {
                    oldLinkage = this.linkage;
                    state = oldLinkage.getState();
                    while (state == Linkage.State.LINKING || state == Linkage.State.NEW) try {
                        wait();
                        oldLinkage = this.linkage;
                        state = oldLinkage.getState();
                    } catch (InterruptedException e) {
                        intr = true;
                    }
                    if (state == Linkage.State.LINKED) {
                        return oldLinkage.getPaths();
                    }
                    this.linkage = linkage = new Linkage(oldLinkage.getDependencySpecs(), oldLinkage.getDependencies(), Linkage.State.LINKING);
                    // fall out and link
                }
                boolean ok = false;
                try {
                    link(linkage);
                    ok = true;
                } finally {
                    if (! ok) {
                        // restore original (lack of) linkage
                        synchronized (this) {
                            if (this.linkage == linkage) {
                                this.linkage = oldLinkage;
                                notifyAll();
                            }
                        }
                    }
                }
            }
        } finally {
            if (intr) {
                Thread.currentThread().interrupt();
            }
        }
    }

    Map<String, List<LocalLoader>> getPathsUnchecked() {
        try {
            return getPaths();
        } catch (ModuleLoadException e) {
            throw e.toError();
        }
    }

    void link(final Linkage linkage) throws ModuleLoadException {
        final HashMap<String, List<LocalLoader>> importsMap = new HashMap<String, List<LocalLoader>>();
        final Dependency[] dependencies = linkage.getDependencies();
        final long start = Metrics.getCurrentCPUTime();
        long subtractTime = 0L;
        try {
            final Set<Visited> visited = new FastCopyHashSet<Visited>(16);
            final FastCopyHashSet<PathFilter> filterStack = new FastCopyHashSet<PathFilter>(8);
            final FastCopyHashSet<ClassFilter> classFilterStack = EMPTY_CLASS_FILTERS;
            final FastCopyHashSet<PathFilter> resourceFilterStack = EMPTY_PATH_FILTERS;
            subtractTime += addPaths(dependencies, importsMap, filterStack, classFilterStack, resourceFilterStack, visited);
            synchronized (this) {
                if (this.linkage == linkage) {
                    this.linkage = new Linkage(linkage.getDependencySpecs(), linkage.getDependencies(), Linkage.State.LINKED, importsMap);
                    notifyAll();
                }
                // else all our efforts were just wasted since someone changed the deps in the meantime
            }
        } finally {
            moduleLoader.addLinkTime(Metrics.getCurrentCPUTime() - start - subtractTime);
        }
    }

    void relinkIfNecessary() throws ModuleLoadException {
        Linkage oldLinkage = this.linkage;
        Linkage linkage;
        if (oldLinkage.getState() != Linkage.State.UNLINKED) {
            return;
        }
        synchronized (this) {
            oldLinkage = this.linkage;
            if (oldLinkage.getState() != Linkage.State.UNLINKED) {
                return;
            }
            this.linkage = linkage = new Linkage(oldLinkage.getDependencySpecs(), oldLinkage.getDependencies(), Linkage.State.LINKING);
        }
        boolean ok = false;
        try {
            link(linkage);
            ok = true;
        } finally {
            if (! ok) {
                // restore original (lack of) linkage
                synchronized (this) {
                    if (this.linkage == linkage) {
                        this.linkage = oldLinkage;
                        notifyAll();
                    }
                }
            }
        }
    }

    void relink() throws ModuleLoadException {
        link(linkage);
    }

    void setDependencies(final List<DependencySpec> dependencySpecs) {
        if (dependencySpecs == null) {
            throw new IllegalArgumentException("dependencySpecs is null");
        }
        final DependencySpec[] specs = dependencySpecs.toArray(new DependencySpec[dependencySpecs.size()]);
        for (DependencySpec spec : specs) {
            if (spec == null) {
                throw new IllegalArgumentException("dependencySpecs contains a null dependency specification");
            }
        }
        setDependencies(specs);
    }

    void setDependencies(final DependencySpec[] dependencySpecs) {
        synchronized (this) {
            linkage = new Linkage(dependencySpecs, calculateDependencies(dependencySpecs), Linkage.State.UNLINKED, null);
            notifyAll();
        }
    }

    private Dependency[] calculateDependencies(final DependencySpec[] dependencySpecs) {
        final Dependency[] dependencies = new Dependency[dependencySpecs.length];
        int i = 0;
        for (DependencySpec spec : dependencySpecs) {
            final Dependency dependency = spec.getDependency(this);
            dependencies[i++] = dependency;
        }
        return dependencies;
    }

    String getMainClass() {
        return mainClassName;
    }

    Package getPackage(final String name) {
        List<LocalLoader> loaders = getPathsUnchecked().get(name.replace('.', '/'));
        if (loaders != null) for (LocalLoader localLoader : loaders) {
            Package pkg = localLoader.loadPackageLocal(name);
            if (pkg != null) return pkg;
        }
        return null;
    }

    Package[] getPackages() {
        final ArrayList<Package> packages = new ArrayList<Package>();
        final Map<String, List<LocalLoader>> allPaths = getPathsUnchecked();
        next: for (String path : allPaths.keySet()) {
            String packageName = path.replace('/', '.');
            for (LocalLoader loader : allPaths.get(path)) {
                Package pkg = loader.loadPackageLocal(packageName);
                if (pkg != null) {
                    packages.add(pkg);
                }
                continue next;
            }
        }
        return packages.toArray(new Package[packages.size()]);
    }
}

redpajama_set_name: RedPajamaGithub


text:
default:
	browserify main.js -o build/all.js


text:
package cn.edu.fudan.mmdb.algorithm;

/**
 * Implements vector methods.
 *
 * @author Pavel Senin
 *
 */
public final class VectorFactory {

  /**
   * Disable constructor.
   */
  private VectorFactory() {
    super();
  }

  /**
   * Get the minimal value of the vector. This implementation will ignore all NaN values in the
   * vector. It will return NaN if all values are NaNs.
   *
   * @param values the vector.
   * @return the vector's minimal value.
   */
  public static double getMinValue(double[] values) {
    double[] tmp_options = new double[values.length];
    int num = 0;

    for (int i = 0; i < values.length; i++) {
      if (Double.isNaN(values[i])) {
        continue;
      }
      else {
        tmp_options[num] = values[i];
        num++;
      }
    }

    double minDistance = Double.NaN;
    if (num != 0) {
      minDistance = Double.MAX_VALUE;
      for (int i = 0; i < num; i++) {
        if (tmp_options[i] < minDistance) {
          minDistance = tmp_options[i];
        }
      }
    }

    return minDistance;
  }

}