package org.applause.lang.validation;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.regex.Pattern;
import org.applause.lang.applauseDsl.ApplauseDslPackage;
import org.applause.lang.applauseDsl.ComplexProviderConstruction;
import org.applause.lang.applauseDsl.Constant;
import org.applause.lang.applauseDsl.ContentProvider;
import org.applause.lang.applauseDsl.Entity;
import org.applause.lang.applauseDsl.Model;
import org.applause.lang.applauseDsl.ObjectReference;
import org.applause.lang.applauseDsl.Parameter;
import org.applause.lang.applauseDsl.ProviderConstruction;
import org.applause.lang.applauseDsl.SimpleProviderConstruction;
import org.applause.lang.applauseDsl.StringLiteral;
import org.applause.lang.applauseDsl.TabbarButton;
import org.applause.lang.applauseDsl.TypeDescription;
import org.applause.lang.applauseDsl.VariableDeclaration;
import org.applause.lang.applauseDsl.View;
import org.applause.lang.applauseDsl.ViewCall;
import org.applause.lang.scoping.TypeUtil;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.CheckType;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
public class ApplauseDslJavaValidator extends AbstractApplauseDslJavaValidator {
public static final String VIEW_NAME_UPPERCASE = "viewname_uppercase";
@Check
void viewNamesShouldStartWithCapital(View view) {
if (!Character.isUpperCase(view.getName().charAt(0))) {
error("View names should start with an uppercase letter.", ApplauseDslPackage.VIEW__NAME, VIEW_NAME_UPPERCASE);
}
}
private static final Pattern LEGAL_FILENAMES_PATTERN = Pattern.compile("[a-z0-9_.]*");
@Check
void validIconFilename(TabbarButton button) {
if (button.getIcon() instanceof StringLiteral) {
String filename = ((StringLiteral) button.getIcon()).getValue();
Resource res = button.eResource();
URI uri = res.getURI().appendSegment("..").appendSegment("..").appendSegment("Images").appendSegment(filename);
boolean exists = (res.getResourceSet().getURIConverter().exists(uri, null));
if(!exists) {
error("Icon file '" + filename + "' does not exist.", ApplauseDslPackage.TABBAR_BUTTON__ICON);
}
else {
if (!LEGAL_FILENAMES_PATTERN.matcher(filename).matches()) {
error("Icon file '" + filename + "' is not a valid filename. " +
"Make sure icon files only contain lowercase letters, numbers and the underscore.", ApplauseDslPackage.TABBAR_BUTTON__ICON);
}
}
}
}
@Check(CheckType.FAST)
void oneResolverPerType(final ContentProvider provider) {
if(!provider.isResolver())
return;
Model model = (Model) provider.eContainer();
Iterable<ContentProvider> allProviders = Iterables.filter(model.getElements(), ContentProvider.class);
Predicate<ContentProvider> otherProviderOfSameType = new Predicate<ContentProvider>() {
public boolean apply(ContentProvider cp) {
return cp.isResolver() && cp != provider && cp.getType() == provider.getType();
}
};
if(Iterables.any(allProviders, otherProviderOfSameType)) {
error("Only one resolver per type allowed", ApplauseDslPackage.CONTENT_PROVIDER__TYPE);
}
}
@Check(CheckType.FAST)
void resolverMustNotReturnLists(ContentProvider provider) {
if(provider.isResolver() && provider.isMany()) {
error("Resolver must not return lists", ApplauseDslPackage.CONTENT_PROVIDER__MANY);
}
}
@Check(CheckType.FAST)
void resolverMustReferToExactlyOneSimpleAttribute(ContentProvider provider) {
if(!provider.isResolver())
return;
Function<? super ObjectReference, ? extends VariableDeclaration> objRefToDeclaration = new Function<ObjectReference, VariableDeclaration>() {
public VariableDeclaration apply(ObjectReference from) {
return from.getObject();
}
};
Iterable<VariableDeclaration> allDeclarations = Iterables.transform(TypeUtil.getReferencesIn(provider.getUrl()), objRefToDeclaration);
Iterable<VariableDeclaration> withoutConstants = Iterables.filter(allDeclarations, new Predicate<VariableDeclaration>() {
public boolean apply(VariableDeclaration d) {
return !(d instanceof Constant);
}
});
Set<VariableDeclaration> declarations = ImmutableSet.copyOf(withoutConstants);
if(declarations.size()<=0)
error("Resolver must use an attribute", ApplauseDslPackage.CONTENT_PROVIDER__URL);
if(declarations.size()>1)
error("Resolver must not use more than one attribute", ApplauseDslPackage.CONTENT_PROVIDER__URL);
}
@Check
void resolversInAndOutIdentical(ContentProvider cp) {
if(cp.isResolver() && !(
cp.getType() == cp.getParameter().getDescription().getType() &&
cp.isMany() == cp.getParameter().getDescription().isMany()))
error("Resolvers input and output types must match", ApplauseDslPackage.CONTENT_PROVIDER__TYPE);
}
public static ContentProvider findResolver(SimpleProviderConstruction construction) {
TypeDescription typeDescription = TypeUtil.getTypeOf(construction.getExpression());
TypeUtil.getTypeOf(construction.getExpression());
final Entity e = (Entity) typeDescription.getType();
Model model = (Model) e.eContainer();
Iterable<ContentProvider> allProviders = Iterables.filter(model.getElements(), ContentProvider.class);
Predicate<ContentProvider> matchingResolvers = new Predicate<ContentProvider>() {
public boolean apply(ContentProvider cp) {
return cp.isResolver() && cp.getType() == e && !cp.isMany();
}
};
try {
return Iterables.find(allProviders, matchingResolvers);
} catch(NoSuchElementException exception) {
return null;
}
}
@Check
void resolverExistsForDirectViewcall(SimpleProviderConstruction construction) {
TypeDescription typeDescription = TypeUtil.getTypeOf(construction.getExpression());
if (typeDescription.getType() instanceof Entity) {
if(findResolver(construction) == null)
warning("No matching resolver found for " + typeDescription.getType().getName(), ApplauseDslPackage.SIMPLE_PROVIDER_CONSTRUCTION__EXPRESSION);
}
}
@Check
void contentProvidersSelectIsLiteral(ContentProvider provider) {
if(!(provider.getSelection() instanceof StringLiteral))
error("selection must be a string literal", ApplauseDslPackage.CONTENT_PROVIDER__SELECTION);
}
private void errorIfNotAssignable(TypeDescription actualType,
TypeDescription expectedType, int feature) {
if(!TypeUtil.isAssignable(expectedType, actualType)) {
error("Type mismatch: cannot covert from " + TypeUtil.asReadableString(actualType)
+ " to " + TypeUtil.asReadableString(expectedType),
feature);
}
}
boolean errorIfCountOfArgumentsDontMatch(boolean expected, boolean actual, int feature) {
if(expected && !actual)
error("Expects argument but nothing was passed", feature);
if(!expected && actual)
error("No argument expected but an argument was passed", feature);
return expected != actual;
}
@Check
void viewsArgumentOfCorrectType(ViewCall vc) {
Parameter formalParameter = vc.getView().getContent();
ProviderConstruction actualParameter = vc.getProvider();
if(! errorIfCountOfArgumentsDontMatch(formalParameter!=null, actualParameter!=null, ApplauseDslPackage.VIEW_CALL__PROVIDER) ) {
TypeDescription expectedType = formalParameter.getDescription();
TypeDescription actualType = TypeUtil.getTypeOf(vc.getProvider());
errorIfNotAssignable(actualType, expectedType, ApplauseDslPackage.VIEW_CALL__PROVIDER);
}
}
@Check
void contentProvidersArgumentOfCorrectType(ComplexProviderConstruction pc) {
ContentProvider p = pc.getProvider();
if(! errorIfCountOfArgumentsDontMatch(p.getParameter() != null, pc.getArgument() != null, ApplauseDslPackage.COMPLEX_PROVIDER_CONSTRUCTION__ARGUMENT)) {
TypeDescription expectedType = TypeUtil.getTypeOf(p.getParameter());
TypeDescription actualType = TypeUtil.getTypeOf(pc.getArgument());
errorIfNotAssignable(actualType, expectedType, ApplauseDslPackage.COMPLEX_PROVIDER_CONSTRUCTION__ARGUMENT);
}
}
}