blob: f03df7fdb7f7123ed60d289ea377779877beb252 [file] [log] [blame]
/*
* Copyright (c) 2014 Google Inc.
*
* All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
* Public License v1.0 which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.google.eclipse.protobuf.validation;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet;
import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__PATH;
import static com.google.eclipse.protobuf.validation.Messages.importNotFound;
import static com.google.eclipse.protobuf.validation.Messages.importingUnsupportedSyntax;
import static java.lang.String.format;
import static org.eclipse.xtext.util.Tuples.pair;
import com.google.eclipse.protobuf.model.util.Imports;
import com.google.eclipse.protobuf.model.util.Protobufs;
import com.google.eclipse.protobuf.model.util.Resources;
import com.google.eclipse.protobuf.protobuf.Import;
import com.google.eclipse.protobuf.protobuf.Protobuf;
import com.google.inject.Inject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.validation.AbstractDeclarativeValidator;
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.EValidatorRegistrar;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Verifies that "imports" contain correct values.
*
* @author alruiz@google.com (Alex Ruiz)
*/
public class ImportValidator extends AbstractDeclarativeValidator {
@Inject private Imports imports;
@Inject private Protobufs protobufs;
@Inject private Resources resources;
@Override public void register(EValidatorRegistrar registrar) {}
/**
* Verifies that {@code Import}s in the given root only refer to files with a supported syntax. If
* unsupported {@code Import}s are found, this validator will create warning markers for such
* {@code Import}s.
*
* @param root the root containing the imports to check.
*/
@Check public void checkUnknownSyntaxImports(Protobuf root) {
if (!protobufs.hasKnownSyntax(root)) {
return;
}
Set<Protobuf> currentlyChecking = newHashSet(root);
HashMap<Protobuf, HasKnownSyntax> alreadyChecked = newHashMap();
hasUnknownSyntaxImports(root, currentlyChecking, alreadyChecked);
}
private boolean hasUnknownSyntaxImports(Protobuf root, Set<Protobuf> currentlyChecking,
Map<Protobuf, HasKnownSyntax> alreadyChecked) {
HasKnownSyntax hasKnownSyntax = alreadyChecked.get(root);
if (hasKnownSyntax != null) {
return hasKnownSyntax == HasKnownSyntax.NO;
}
currentlyChecking.add(root);
Set<Pair<Import, Protobuf>> importsToCheck = newHashSet();
boolean hasUnsupportedImports = false;
for (Import anImport : protobufs.importsIn(root)) {
Resource imported = imports.importedResource(anImport);
if (imported == null) {
continue;
}
Protobuf importedRoot = resources.rootOf(imported);
hasKnownSyntax = alreadyChecked.get(importedRoot);
if (hasKnownSyntax != null) {
// resource was already checked.
if (hasKnownSyntax == HasKnownSyntax.NO) {
hasUnsupportedImports = true;
warnUnsupportedImportFoundIn(anImport);
}
continue;
}
if (!protobufs.hasKnownSyntax(importedRoot)) {
alreadyChecked.put(importedRoot, HasKnownSyntax.NO);
hasUnsupportedImports = true;
warnUnsupportedImportFoundIn(anImport);
continue;
}
// we have a circular dependency
if (currentlyChecking.contains(importedRoot)) {
continue;
}
// this is a supported file. Need to check its imports.
importsToCheck.add(pair(anImport, importedRoot));
}
for (Pair<Import, Protobuf> importToCheck : importsToCheck) {
if (hasUnknownSyntaxImports(importToCheck.getSecond(), currentlyChecking, alreadyChecked)) {
hasUnsupportedImports = true;
warnUnsupportedImportFoundIn(importToCheck.getFirst());
}
}
hasKnownSyntax = hasUnsupportedImports ? HasKnownSyntax.NO : HasKnownSyntax.YES;
alreadyChecked.put(root, hasKnownSyntax);
currentlyChecking.remove(root);
return hasUnsupportedImports;
}
private void warnUnsupportedImportFoundIn(Import anImport) {
warning(importingUnsupportedSyntax, anImport, IMPORT__PATH, INSIGNIFICANT_INDEX);
}
/**
* Verifies that the URI of the given {@code Import} has been resolved. If the URI has not been
* resolved, this validator will create an error marker for the given {@code Import}.
*
* @param anImport the given {@code Import}.
*/
@Check
public void checkUriIsResolved(Import anImport) {
if (imports.isResolved(anImport)) {
return;
}
error(format(importNotFound, imports.getPath(anImport)), IMPORT__PATH);
}
private static enum HasKnownSyntax {
YES, NO;
}
}