Skip to content

Commit

Permalink
Merge pull request #1599 from usethesource/fallback-resolver
Browse files Browse the repository at this point in the history
Fallback resolvers added to the URIRegistryResolver
  • Loading branch information
DavyLandman authored May 17, 2022
2 parents 1ae41a3 + 7d99413 commit 8f8afa8
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 74 deletions.
7 changes: 5 additions & 2 deletions src/org/rascalmpl/library/Prelude.java
Original file line number Diff line number Diff line change
Expand Up @@ -1333,10 +1333,13 @@ private void writeFileEnc(ISourceLocation sloc, IString charset, IList V, boolea
try {
sloc = reg.logicalToPhysical(sloc);

if (reg.supportsInputScheme(sloc.getScheme())) {
if (sloc.hasOffsetLength()) {

if (sloc.hasOffsetLength()) {
try {
prefix = new UnicodeOffsetLengthReader(reg.getCharacterReader(sloc.top(), charset.getValue()), 0, sloc.getOffset() + ( append ? sloc.getLength() : 0 ));
postfix = new UnicodeOffsetLengthReader(reg.getCharacterReader(sloc.top(), charset.getValue()), sloc.getOffset() + sloc.getLength(), -1);
} catch (UnsupportedSchemeException e) {
// silently ignoring that we cannot do an append for this scheme as there is no input stream resolver defined for this scheme
}
}

Expand Down
206 changes: 134 additions & 72 deletions src/org/rascalmpl/uri/URIResolverRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.rascalmpl.unicode.UnicodeInputStreamReader;
import org.rascalmpl.unicode.UnicodeOffsetLengthReader;
import org.rascalmpl.uri.ISourceLocationWatcher.ISourceLocationChanged;
Expand All @@ -57,6 +59,14 @@ public class URIResolverRegistry {
private final Map<String, ISourceLocationWatcher> watchers = new ConcurrentHashMap<>();
private final Map<ISourceLocation, Set<Consumer<ISourceLocationChanged>>> watching = new ConcurrentHashMap<>();

// we allow the user to define (using -Drascal.fallbackResolver=fully.qualified.classname) a single class that will handle
// scheme's not statically registered. That class should implement at least one of these interfaces
private volatile @Nullable ISourceLocationInput fallbackInputResolver;
private volatile @Nullable ISourceLocationOutput fallbackOutputResolver;
private volatile @Nullable ILogicalSourceLocationResolver fallbackLogicalResolver;
private volatile @Nullable IClassloaderLocationResolver fallbackClassloaderResolver;
private volatile @Nullable ISourceLocationWatcher fallbackWatcher;

private static class InstanceHolder {
static URIResolverRegistry sInstance = new URIResolverRegistry();
}
Expand Down Expand Up @@ -98,6 +108,10 @@ private void loadServices() {
try {
Enumeration<URL> resources = getClass().getClassLoader().getResources(RESOLVERS_CONFIG);
Collections.list(resources).forEach(f -> loadServices(f));
var fallbackResolverClassName = System.getProperty("rascal.fallbackResolver");
if (fallbackResolverClassName != null) {
loadFallback(fallbackResolverClassName);
}
}
catch (IOException e) {
throw new Error("WARNING: Could not load URIResolverRegistry extensions from " + RESOLVERS_CONFIG, e);
Expand All @@ -120,6 +134,57 @@ public Set<String> getRegisteredClassloaderSchemes() {
return Collections.unmodifiableSet(classloaderResolvers.keySet());
}

private Object constructService(String name) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException {
Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(name);

try {
return clazz.getDeclaredConstructor(URIResolverRegistry.class).newInstance(this);
}
catch (NoSuchMethodException e) {
return clazz.newInstance();
}
}

private void loadFallback(String fallbackClass) {
try {
Object instance = constructService(fallbackClass);
boolean ok = false;
if (instance instanceof ILogicalSourceLocationResolver) {
fallbackLogicalResolver = (ILogicalSourceLocationResolver) instance;
ok = true;
}

if (instance instanceof ISourceLocationInput) {
fallbackInputResolver = (ISourceLocationInput) instance;
ok = true;
}

if (instance instanceof ISourceLocationOutput) {
fallbackOutputResolver = (ISourceLocationOutput) instance;
ok = true;
}

if (instance instanceof IClassloaderLocationResolver) {
fallbackClassloaderResolver = (IClassloaderLocationResolver) instance;
ok = true;
}

if (instance instanceof ISourceLocationWatcher) {
fallbackWatcher = (ISourceLocationWatcher) instance;
}
if (!ok) {
System.err.println("WARNING: could not load fallback resolver " + fallbackClass
+ " because it does not implement ISourceLocationInput or ISourceLocationOutput or ILogicalSourceLocationResolver");
}
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException | ClassCastException
| IllegalArgumentException | InvocationTargetException | SecurityException e) {
System.err.println("WARNING: could not load resolver due to " + e.getMessage());
e.printStackTrace();
}

}

private void loadServices(URL nextElement) {
try {
for (String name : readConfigFile(nextElement)) {
Expand All @@ -130,15 +195,7 @@ private void loadServices(URL nextElement) {
continue;
}

Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(name);
Object instance;

try {
instance = clazz.getDeclaredConstructor(URIResolverRegistry.class).newInstance(this);
}
catch (NoSuchMethodException e) {
instance = clazz.newInstance();
}
Object instance = constructService(name);

boolean ok = false;

Expand Down Expand Up @@ -268,75 +325,85 @@ public ISourceLocation logicalToPhysical(ISourceLocation loc) throws IOException
return result;
}

private ISourceLocation physicalLocation(ISourceLocation loc) throws IOException {
while (logicalResolvers.containsKey(loc.getScheme())) {
Map<String, ILogicalSourceLocationResolver> map = logicalResolvers.get(loc.getScheme());
String auth = loc.hasAuthority() ? loc.getAuthority() : "";
ILogicalSourceLocationResolver resolver = map.get(auth);
ISourceLocation prev = loc;
boolean removedOffset = false;
private static ISourceLocation resolveAndFixOffsets(ISourceLocation loc, ILogicalSourceLocationResolver resolver, Iterable<ILogicalSourceLocationResolver> backups) throws IOException {
ISourceLocation prev = loc;
boolean removedOffset = false;

if (resolver != null) {
loc = resolver.resolve(loc);
}
if (resolver != null) {
loc = resolver.resolve(loc);
}

if (loc == null && prev.hasOffsetLength()) {
loc = resolver.resolve(URIUtil.removeOffset(prev));
removedOffset = true;
}
if (loc == null && prev.hasOffsetLength()) {
loc = resolver.resolve(URIUtil.removeOffset(prev));
removedOffset = true;
}

if (loc == null || prev.equals(loc)) {
for (ILogicalSourceLocationResolver backup : map.values()) {
removedOffset = false;
loc = backup.resolve(prev);
if (loc == null || prev.equals(loc)) {
for (ILogicalSourceLocationResolver backup : backups) {
removedOffset = false;
loc = backup.resolve(prev);

if (loc == null && prev.hasOffsetLength()) {
loc = backup.resolve(URIUtil.removeOffset(prev));
removedOffset = true;
}
if (loc == null && prev.hasOffsetLength()) {
loc = backup.resolve(URIUtil.removeOffset(prev));
removedOffset = true;
}

if (loc != null && !prev.equals(loc)) {
break; // continue to offset/length handling below with found location
}
if (loc != null && !prev.equals(loc)) {
break; // continue to offset/length handling below with found location
}
}
}

if (loc == null || prev.equals(loc)) {
return null;
}
if (loc == null || prev.equals(loc)) {
return null;
}

if (removedOffset || !loc.hasOffsetLength()) { // then copy the offset from the logical one
if (prev.hasLineColumn()) {
loc = vf.sourceLocation(loc, prev.getOffset(), prev.getLength(), prev.getBeginLine(),
prev.getEndLine(), prev.getBeginColumn(), prev.getEndColumn());
}
else if (prev.hasOffsetLength()) {
if (loc.hasOffsetLength()) {
loc = vf.sourceLocation(loc, prev.getOffset() + loc.getOffset(), prev.getLength());
}
else {
loc = vf.sourceLocation(loc, prev.getOffset(), prev.getLength());
}
}
if (removedOffset || !loc.hasOffsetLength()) { // then copy the offset from the logical one
if (prev.hasLineColumn()) {
return vf.sourceLocation(loc, prev.getOffset(), prev.getLength(), prev.getBeginLine(),
prev.getEndLine(), prev.getBeginColumn(), prev.getEndColumn());
}
else if (loc.hasLineColumn()) { // the logical location offsets relative to the physical offset, possibly
// including line numbers
if (prev.hasLineColumn()) {
loc = vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength(),
loc.getBeginLine() + prev.getBeginLine() - 1, loc.getEndLine() + prev.getEndLine() - 1,
loc.getBeginColumn(), loc.getEndColumn());
else if (prev.hasOffsetLength()) {
if (loc.hasOffsetLength()) {
return vf.sourceLocation(loc, prev.getOffset() + loc.getOffset(), prev.getLength());
}
else if (prev.hasOffsetLength()) {
loc = vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength());
else {
return vf.sourceLocation(loc, prev.getOffset(), prev.getLength());
}
}
else if (loc.hasOffsetLength()) { // the logical location offsets relative to the physical one
if (prev.hasOffsetLength()) {
loc = vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength());
}
}
else if (loc.hasLineColumn()) { // the logical location offsets relative to the physical offset, possibly
// including line numbers
if (prev.hasLineColumn()) {
return vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength(),
loc.getBeginLine() + prev.getBeginLine() - 1, loc.getEndLine() + prev.getEndLine() - 1,
loc.getBeginColumn(), loc.getEndColumn());
}
else if (prev.hasOffsetLength()) {
return vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength());
}
}
else if (loc.hasOffsetLength()) { // the logical location offsets relative to the physical one
if (prev.hasOffsetLength()) {
return vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength());
}
}
// otherwise we return the loc without any offsets
return loc;
}

private ISourceLocation physicalLocation(ISourceLocation loc) throws IOException {
ISourceLocation original = loc;
while (loc != null && logicalResolvers.containsKey(loc.getScheme())) {
Map<String, ILogicalSourceLocationResolver> map = logicalResolvers.get(loc.getScheme());
String auth = loc.hasAuthority() ? loc.getAuthority() : "";
ILogicalSourceLocationResolver resolver = map.get(auth);
loc = resolveAndFixOffsets(loc, resolver, map.values());
}
var fallBack = fallbackLogicalResolver;
if (fallBack != null) {
return resolveAndFixOffsets(loc == null ? original : loc, fallBack, Collections.emptyList());
}
return loc;
}

Expand Down Expand Up @@ -392,6 +459,7 @@ private ISourceLocationInput getInputResolver(String scheme) {
String subScheme = m.group(1);
return inputResolvers.get(subScheme);
}
return fallbackInputResolver;
}
return result;
}
Expand All @@ -404,6 +472,7 @@ private IClassloaderLocationResolver getClassloaderResolver(String scheme) {
String subScheme = m.group(1);
return classloaderResolvers.get(subScheme);
}
return fallbackClassloaderResolver;
}
return result;
}
Expand All @@ -416,18 +485,11 @@ private ISourceLocationOutput getOutputResolver(String scheme) {
String subScheme = m.group(1);
return outputResolvers.get(subScheme);
}
return fallbackOutputResolver;
}
return result;
}

public boolean supportsInputScheme(String scheme) {
return getInputResolver(scheme) != null;
}

public boolean supportsOutputScheme(String scheme) {
return getOutputResolver(scheme) != null;
}

public boolean supportsHost(ISourceLocation uri) {
uri = safeResolve(uri);
ISourceLocationInput resolver = getInputResolver(uri.getScheme());
Expand Down Expand Up @@ -864,7 +926,7 @@ public void watch(ISourceLocation loc, boolean recursive, Consumer<ISourceLocati
loc = URIUtil.getParentLocation(loc);
}

ISourceLocationWatcher watcher = watchers.get(loc.getScheme());
ISourceLocationWatcher watcher = watchers.getOrDefault(loc.getScheme(), fallbackWatcher);
if (watcher != null) {
watcher.watch(loc, callback);
}
Expand Down Expand Up @@ -895,7 +957,7 @@ public void unwatch(ISourceLocation loc, boolean recursive, Consumer<ISourceLoca
// watching directories (the native NEO file watchers are like that)
loc = URIUtil.getParentLocation(loc);
}
ISourceLocationWatcher watcher = watchers.get(loc.getScheme());
ISourceLocationWatcher watcher = watchers.getOrDefault(loc.getScheme(), fallbackWatcher);
if (watcher != null) {
watcher.unwatch(loc, callback);
}
Expand Down

0 comments on commit 8f8afa8

Please sign in to comment.