/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.runtime.modules.loader; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; import com.github.anba.es6draft.runtime.Realm; import com.github.anba.es6draft.runtime.internal.RuntimeContext; import com.github.anba.es6draft.runtime.modules.MalformedNameException; import com.github.anba.es6draft.runtime.modules.ModuleLoader; import com.github.anba.es6draft.runtime.modules.ModuleRecord; import com.github.anba.es6draft.runtime.modules.ModuleSource; import com.github.anba.es6draft.runtime.modules.SourceIdentifier; /** * */ public abstract class AbstractModuleLoader<MODULE extends ModuleRecord> implements ModuleLoader { private final RuntimeContext context; protected AbstractModuleLoader(RuntimeContext context) { this.context = context; } /** * Returns the runtime context for this loader. * * @return the runtime context */ protected RuntimeContext getContext() { return context; } /** * Loads the module source. * * @param identifier * the source identifier * @return the module source object * @throws IOException * if there was any I/O error */ protected abstract ModuleSource loadSource(SourceIdentifier identifier) throws IOException; /** * Adds a module record to the internal modules list. * * @param module * the module record */ protected abstract void defineModule(MODULE module); /** * Retrieves a module record from the internal modules list. * * @param identifier * the source identifier * @return the module record or {@code null} if not found */ protected abstract MODULE getModule(SourceIdentifier identifier); /** * Parses a module record. * * @param identifier * the source identifier * @param source * the module source * @return the parsed module record * @throws IOException * if there was any I/O error */ protected abstract MODULE parseModule(SourceIdentifier identifier, ModuleSource source) throws IOException; /** * Links the module against the realm instance. * * @param module * the module record * @param realm * the realm instance */ protected abstract void linkModule(MODULE module, Realm realm); /** * Returns the list of requested modules. * * @param module * the module record * @return the list of unnormalized module names */ protected abstract Set<String> getRequestedModules(MODULE module); /** * Returns an unmodifiable collection of the currently loaded modules. * <p> * Optional operation: Returns an empty collection if not supported. * * @return the module collection */ protected Collection<MODULE> getModules() { return Collections.emptySet(); } @Override public MODULE get(SourceIdentifier identifier, Realm realm) { MODULE module = getModule(identifier); if (module != null) { linkModule(module, realm); } return module; } @Override public MODULE define(SourceIdentifier identifier, ModuleSource source, Realm realm) throws IOException { MODULE module = getModule(identifier); if (module == null) { module = parseModule(identifier, source); defineModule(module); } linkModule(module, realm); return module; } @Override public MODULE resolve(SourceIdentifier identifier, Realm realm) throws IOException { MODULE module = loadIfAbsent(identifier); linkModule(module, realm); return module; } @Override public MODULE load(SourceIdentifier identifier) throws MalformedNameException, IOException { MODULE module = loadIfAbsent(identifier); loadRequested(module, new HashSet<>()); return module; } private MODULE loadIfAbsent(SourceIdentifier identifier) throws IOException { MODULE module = getModule(identifier); if (module == null) { module = parseModule(identifier, loadSource(identifier)); defineModule(module); } return module; } private void loadRequested(MODULE module, HashSet<ModuleRecord> visited) throws MalformedNameException, IOException { if (visited.add(module)) { SourceIdentifier referrerId = module.getSourceCodeId(); for (String specifier : getRequestedModules(module)) { SourceIdentifier identifier = normalizeName(specifier, referrerId); loadRequested(loadIfAbsent(identifier), visited); } } } }