/******************************************************************************* * Copyright (c) 2011 BestSolution.at and others. * 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 * * Contributors: * Tom Schindl<tom.schindl@bestsolution.at> - initial API and implementation *******************************************************************************/ package at.bestsolution.efxclipse.tooling.css; import static at.bestsolution.efxclipse.tooling.css.CssDialectExtension.Util.fromList; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.osgi.framework.Version; import at.bestsolution.efxclipse.tooling.css.cssDsl.CssDslPackage; import at.bestsolution.efxclipse.tooling.css.cssDsl.URLType; import at.bestsolution.efxclipse.tooling.css.cssDsl.css_declaration; import at.bestsolution.efxclipse.tooling.css.cssDsl.css_generic_declaration; import at.bestsolution.efxclipse.tooling.css.cssDsl.term; import at.bestsolution.efxclipse.tooling.css.cssDsl.termGroup; public interface CssDialectExtension { public enum ValidationStatus { OK, WARNING, ERROR } public static class ValidationResult { public final ValidationStatus status; public final String message; public final EObject object; public final EStructuralFeature feature; public final int index; public ValidationResult(ValidationStatus status, String message, EObject object, EStructuralFeature feature, int index) { this.status = status; this.message = message; this.object = object; this.feature = feature; this.index = index; } @Override public String toString() { return status.name() + " - " + message; } } public abstract static class Property { private final String name; private final String description; private final Version minVersion; public Property(String name) { this(name,new Version("2.0.0")); } public Property(String name, Version minVersion) { this(name, null,new Version("2.0.0")); } public Property(String name, String description) { this(name, description,new Version("2.0.0")); } public Property(String name, String description, Version minVersion) { this.name = name; this.description = description; this.minVersion = minVersion == null ? new Version("2.0.0") : minVersion; } public Version getMinVersion() { return minVersion; } public String getName() { return name; } public String getDescription() { return description; } public abstract List<Proposal> getInitialTermProposals(); public ValidationResult[] validate(css_generic_declaration dec) { return new ValidationResult[0]; } } public interface MultiValuesGroupProperty { public List<Proposal> getNextTermProposal(int index, termGroup currentGroup, term term); } public interface MultiTermGroupProperty { public List<Proposal> getInitialTermProposal(int index, css_declaration currentDeclaration); } public static class IntegerProperty extends Property { private List<Proposal> proposals = new ArrayList<Proposal>(); public IntegerProperty(String name) { super(name); proposals.add(new Proposal("0")); proposals.add(new Proposal("1")); proposals.add(new Proposal("inherit")); } @Override public List<Proposal> getInitialTermProposals() { return proposals; } @Override public ValidationResult[] validate(css_generic_declaration dec) { if( dec.getExpression() != null ) { if( dec.getExpression().getTermGroups().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple term groups", null, null, -1) }; } else if( dec.getExpression().getTermGroups().size() == 1 ) { if( dec.getExpression().getTermGroups().get(0).getTerms().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple terms", null, null, -1) }; } else if( dec.getExpression().getTermGroups().get(0).getTerms().size() == 1 ) { String number = dec.getExpression().getTermGroups().get(0).getTerms().get(0).getNumber(); if( number == null ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value is not a integer value", null, null, -1) }; } else { try { double d = Double.parseDouble(number); if( d != (int)d ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.WARNING, "The value is floating point number but should be an integer", null, null, -1) }; } } catch( NumberFormatException e ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value is not a integer value", null, null, -1) }; } } } } } return super.validate(dec); } } public static class StringProperty extends Property { private List<Proposal> proposals = new ArrayList<Proposal>(); public StringProperty(String name) { super(name); } @Override public List<Proposal> getInitialTermProposals() { return proposals; } } public static class BooleanProperty extends Property { private List<Proposal> proposals = new ArrayList<Proposal>(); public BooleanProperty(String name) { super(name); proposals.addAll(fromList("true","false")); } @Override public List<Proposal> getInitialTermProposals() { return proposals; } @Override public ValidationResult[] validate(css_generic_declaration dec) { if( dec.getExpression() != null ) { if( dec.getExpression().getTermGroups().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple term groups", null, null, -1) }; } else if( dec.getExpression().getTermGroups().size() == 1 ) { if( dec.getExpression().getTermGroups().get(0).getTerms().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple terms", null, null, -1) }; } else if( dec.getExpression().getTermGroups().get(0).getTerms().size() == 1 ) { String value = dec.getExpression().getTermGroups().get(0).getTerms().get(0).getIdentifier(); if( value == null || ! ( value.equals("true") || value.equals("false") ) ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value is true or false", null, null, -1) }; } } } } return super.validate(dec); } } public static class NumberPropery extends Property { private List<Proposal> proposals = new ArrayList<Proposal>(); public NumberPropery(String name) { super(name); } @Override public List<Proposal> getInitialTermProposals() { return proposals; } @Override public ValidationResult[] validate(css_generic_declaration dec) { if( dec.getExpression() != null ) { if( dec.getExpression().getTermGroups().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple term groups", null, null, -1) }; } else if( dec.getExpression().getTermGroups().size() == 1 ) { if( dec.getExpression().getTermGroups().get(0).getTerms().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple terms", null, null, -1) }; } else if( dec.getExpression().getTermGroups().get(0).getTerms().size() == 1 ) { String number = dec.getExpression().getTermGroups().get(0).getTerms().get(0).getNumber(); if( number == null ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value is not a floating point number", null, null, -1) }; } else { try { Double.parseDouble(number); } catch( NumberFormatException e ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value is not a floating point number", null, null, -1) }; } } } } } return super.validate(dec); } } public static class EnumProperty extends Property { private List<Proposal> proposals = new ArrayList<Proposal>(); public EnumProperty(String name, String... enums) { this(name,null,enums); } public EnumProperty(Version minVersion, String name, String... enums) { this(name,null,enums); } public EnumProperty(String name, String description, String... enums) { this(null,name,null,enums); } public EnumProperty(Version minVersion, String name, String description, String... enums) { super(name, description, minVersion); proposals.addAll(fromList(enums)); proposals.add(new Proposal("inherit")); } @Override public List<Proposal> getInitialTermProposals() { return proposals; } @Override public ValidationResult[] validate(css_generic_declaration dec) { if( dec.getExpression() != null ) { if( dec.getExpression().getTermGroups().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple term groups", null, null, -1) }; } else if( dec.getExpression().getTermGroups().size() == 1 ) { if( dec.getExpression().getTermGroups().get(0).getTerms().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple terms", null, null, -1) }; } else if( dec.getExpression().getTermGroups().get(0).getTerms().size() == 1 ) { String id = dec.getExpression().getTermGroups().get(0).getTerms().get(0).getIdentifier(); for( Proposal p : proposals ) { if( p.getProposal().equalsIgnoreCase(id) ) { return new ValidationResult[0]; } } StringBuilder b = new StringBuilder(); for( Proposal p: proposals ) { b.append("- " + p.getProposal()); if( p.getLabel() != null && ! p.getLabel().equals(p.getProposal()) ) { b.append(": " +p.getLabel()); } b.append("\n"); } return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value has to be:\n" + b, null, null, -1) }; } } } return super.validate(dec); } } public static class UrlProperty extends Property { private List<Proposal> proposals = new ArrayList<Proposal>(); public UrlProperty(String name) { super(name); proposals.add(new Proposal("url(\"resource\")")); proposals.add(new Proposal("url('resource')")); } @Override public List<Proposal> getInitialTermProposals() { return proposals; } @Override public ValidationResult[] validate(css_generic_declaration dec) { if( dec.getExpression() != null ) { if( dec.getExpression().getTermGroups().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple term groups", null, null, -1) }; } else if( dec.getExpression().getTermGroups().size() == 1 ) { if( dec.getExpression().getTermGroups().get(0).getTerms().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple terms", null, null, -1) }; } else if( dec.getExpression().getTermGroups().get(0).getTerms().size() == 1 ) { URLType url = dec.getExpression().getTermGroups().get(0).getTerms().get(0).getUrl(); if( url == null || url.getUrl().trim().length() == 0 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value is not an url", null, null, -1) }; } else { try { new java.net.URI(url.getUrl()); } catch (URISyntaxException e) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value is not an url", null, null, -1) }; } } } } } return super.validate(dec); } } public static class UrlsProperty extends Property { private List<Proposal> proposals = new ArrayList<Proposal>(); public UrlsProperty(String name) { super(name); proposals.add(new Proposal("url(\"resource\")")); proposals.add(new Proposal("url('resource')")); } @Override public List<Proposal> getInitialTermProposals() { return proposals; } @Override public ValidationResult[] validate(css_generic_declaration dec) { if( dec.getExpression() != null ) { for( termGroup g : dec.getExpression().getTermGroups() ) { if( g.getTerms().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple terms", null, null, -1) }; } else if( g.getTerms().size() == 1 ) { URLType url = g.getTerms().get(0).getUrl(); if( url == null || url.getUrl().trim().length() == 0 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value is not an url", g.getTerms().get(0), CssDslPackage.Literals.TERM__URL, -1) }; } else { try { new java.net.URI(url.getUrl()); } catch (URISyntaxException e) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value is not an url", null, null, -1) }; } } } } } return super.validate(dec); } } public static class EnumsProperty extends Property implements MultiValuesGroupProperty { private List<Proposal> proposals = new ArrayList<Proposal>(); private List<Proposal> singleTerms = new ArrayList<Proposal>(); private int partCount; public EnumsProperty(String name, int partCount, String... enums) { this(name,null,partCount,enums); } public EnumsProperty(String name, String description, int partCount, String... enums) { super(name, description); this.partCount = partCount; for( String v : enums ) { StringBuilder b = new StringBuilder(); for( int i = 0; i < partCount; i++ ) { if( b.length() > 0 ) { b.append(" "); } b.append(v); } proposals.add(new Proposal(v)); proposals.add(new Proposal(b.toString())); singleTerms.add(new Proposal(v)); } } @Override public List<Proposal> getInitialTermProposals() { return proposals; } @Override public List<Proposal> getNextTermProposal(int index, termGroup currentGroup, term term) { if( index < partCount ) { return singleTerms; } return Collections.emptyList(); } @Override public ValidationResult[] validate(css_generic_declaration dec) { if( dec.getExpression() != null ) { if( dec.getExpression().getTermGroups().size() > 1 ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The attribute does not support multiple term groups", null, null, -1) }; } else if( dec.getExpression().getTermGroups().size() == 1 ) { if( dec.getExpression().getTermGroups().get(0).getTerms().size() == 1 ) { String value = dec.getExpression().getTermGroups().get(0).getTerms().get(0).getIdentifier(); StringBuilder b = new StringBuilder(); for( Proposal p: singleTerms ) { b.append("- " + p.getProposal()); if( p.getLabel() != null && ! p.getLabel().equals(p.getProposal()) ) { b.append(": " +p.getLabel()); } b.append("\n"); } if( value == null ) { return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value has to be:\n" + b, null, null, -1) }; } else if( ! "inherit".equals(value) ) { for( Proposal p : singleTerms ) { if( value.equals(p.getProposal()) ) { return super.validate(dec); } } return new ValidationResult[] { new ValidationResult(ValidationStatus.ERROR, "The value has to be:\n" + b, dec.getExpression().getTermGroups().get(0).getTerms().get(0), CssDslPackage.Literals.TERM__IDENTIFIER, -1) }; } } else if( dec.getExpression().getTermGroups().get(0).getTerms().size() == partCount ) { StringBuilder b = new StringBuilder(); for( Proposal p: singleTerms ) { b.append("- " + p.getProposal()); if( p.getLabel() != null && ! p.getLabel().equals(p.getProposal()) ) { b.append(": " +p.getLabel()); } b.append("\n"); } List<ValidationResult> rv = new ArrayList<ValidationResult>(); for( term t : dec.getExpression().getTermGroups().get(0).getTerms() ) { String value = t.getIdentifier(); if( value == null ) { rv.add(new ValidationResult(ValidationStatus.ERROR, "The value has to be:\n" + b, null, null, -1)); } else { boolean v = false; for( Proposal p : singleTerms ) { if( value.equals(p.getProposal()) ) { v = true; } } if( ! v ) { rv.add(new ValidationResult(ValidationStatus.ERROR, "The value has to be:\n" + b, t, CssDslPackage.Literals.TERM__IDENTIFIER, -1) ); } } } if( rv.isEmpty() ) { return super.validate(dec); } else { return rv.toArray(new ValidationResult[0]); } } } } return super.validate(dec); } } public static class Proposal { private String proposal; private String label; private String imageUrl; private int priority = 1; public Proposal(String label) { this.proposal = label; this.label = label; } public Proposal(int priority, String label) { this.priority = priority; this.proposal = label; this.label = label; } public String getProposal() { return proposal; } public String getImageUrl() { return imageUrl; } public String getLabel() { return label; } public int getPriority() { return priority; } } public static abstract class DialogProposal extends Proposal { public DialogProposal(String label) { super(label); } public DialogProposal(int priority, String label) { super(priority,label); } public abstract String openProposal(); } public static class Util { public static class DescribedName { private final String name; private final String description; public DescribedName(String name, String description) { this.name = name; this.description = description; } public static DescribedName c(String name, String description) { return new DescribedName(name, description); } } public static List<Proposal> fromList(String... strings) { List<Proposal> rv = new ArrayList<Proposal>(); for( String s : strings ) { rv.add(new Proposal(s)); } return rv; } public static List<Property> createEnumProperties(List<String> enums, String... names) { List<Property> rv = new ArrayList<Property>(names.length); String[] arEnums = enums.toArray(new String[0]); for( String name : names ) { rv.add(new EnumProperty(name, arEnums)); } return rv; } public static List<Property> createEnumProperties(Version minVersion, List<String> enums, String... names) { List<Property> rv = new ArrayList<Property>(names.length); String[] arEnums = enums.toArray(new String[0]); for( String name : names ) { rv.add(new EnumProperty(name, arEnums)); } return rv; } public static List<Property> createEnumProperties(List<String> enums, DescribedName... names) { List<Property> rv = new ArrayList<Property>(names.length); String[] arEnums = enums.toArray(new String[0]); for( DescribedName name : names ) { rv.add(new EnumProperty(name.name, name.description, arEnums)); } return rv; } public static List<Property> createEnumsProperties(List<String> enums, int partCount, String... names) { List<Property> rv = new ArrayList<Property>(names.length); String[] arEnums = enums.toArray(new String[0]); for( String name : names ) { rv.add(new EnumsProperty(name, partCount, arEnums)); } return rv; } public static List<Property> createEnumsProperties(List<String> enums, int partCount, DescribedName... names) { List<Property> rv = new ArrayList<Property>(names.length); String[] arEnums = enums.toArray(new String[0]); for( DescribedName name : names ) { rv.add(new EnumsProperty(name.name, name.description, partCount, arEnums)); } return rv; } public static List<Property> createReflective(Class<? extends Property> clazz, String... names) { List<Property> rv = new ArrayList<Property>(names.length); Constructor<? extends Property> c; try { c = clazz.getConstructor(String.class); for( String name : names ) { rv.add(c.newInstance(name)); } } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return rv; } public static ValidationResult checkNumber(term term, String message) { try { Double.parseDouble(term.getNumber()); } catch (NumberFormatException e) { return new ValidationResult(ValidationStatus.ERROR, message, term, null, -1); } return null; } public static ValidationResult checkPercentage(term term, String message, int min) { if( term.getNumber().matches("^\\d+(\\.\\d+)?%$") || term.getNumber().matches("^-\\d+(\\.\\d+)?%$") || term.getNumber().matches("^\\+\\d+(\\.\\d+)?%$") ) { double v = Double.parseDouble(term.getNumber().substring(0, term.getNumber().length()-1)); if( v < min * 1.0 || v > 100.0 ) { return new ValidationResult(ValidationStatus.ERROR, message, term, null, -1); } } else { return new ValidationResult(ValidationStatus.ERROR, message, term, null, -1); } return null; } } public List<Property> getProperties(); public boolean isActive(URI uri); }