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); } } }