/*********************************************************************************
* Copyright (c) 2013-2015 AKSW Xturtle Project, itemis AG (http://www.itemis.eu).
* 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 de.itemis.tooling.xturtle.validation;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.diagnostics.Severity;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.nodemodel.BidiTreeIterator;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.ILeafNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.CheckType;
import com.google.common.base.Optional;
import com.google.inject.Inject;
import de.itemis.tooling.xturtle.resource.TurtleResourceService;
import de.itemis.tooling.xturtle.services.Prefixes;
import de.itemis.tooling.xturtle.xturtle.BlankObjects;
import de.itemis.tooling.xturtle.xturtle.Directives;
import de.itemis.tooling.xturtle.xturtle.Predicate;
import de.itemis.tooling.xturtle.xturtle.PredicateObjectList;
import de.itemis.tooling.xturtle.xturtle.PrefixId;
import de.itemis.tooling.xturtle.xturtle.QNameDef;
import de.itemis.tooling.xturtle.xturtle.QNameRef;
import de.itemis.tooling.xturtle.xturtle.Resource;
import de.itemis.tooling.xturtle.xturtle.StringLiteral;
import de.itemis.tooling.xturtle.xturtle.Triples;
import de.itemis.tooling.xturtle.xturtle.UriDef;
import de.itemis.tooling.xturtle.xturtle.XturtlePackage;
public class XturtleJavaValidator extends AbstractXturtleJavaValidator {
@Inject
private Prefixes prefixes;
@Inject
private TurtleResourceService service;
@Inject
private TurtleValidationSeverityLevels levels;
@Inject
private TurtleLinkingErrorExceptions linkingErrorExceptions;
public static final String UNKNOWN_PREFIX="unknownPrefix";
@Check(CheckType.NORMAL)
public void checkAxiomSyntax(Triples triples) {
if(!(triples.getSubject() instanceof BlankObjects) && triples.getPredObjs().isEmpty()){
//raise the error only if the subject has no syntax errors
ICompositeNode node = NodeModelUtils.getNode(triples.getSubject());
BidiTreeIterator<INode> iterator = node.getAsTreeIterable().iterator();
while(iterator.hasNext()){
if(iterator.next().getSyntaxErrorMessage()!=null){
return;
}
}
error("predicate object list is optional only for blank",XturtlePackage.Literals.TRIPLES__SUBJECT,"axiom");
}
}
private boolean containsWhitespace(EObject o){
Iterable<ILeafNode> leaves = NodeModelUtils.findActualNodeFor(o).getLeafNodes();
boolean hiddenOK=true;
for (ILeafNode leaf : leaves) {
if(leaf.isHidden()){
if(!hiddenOK){
return true;
}
}else{
hiddenOK=false;
}
}
return false;
}
@Check(CheckType.EXPENSIVE)
public void checkNoWhitespaceInQName(QNameRef qnameRef) {
if(containsWhitespace(qnameRef)){
error("qname must not contain whitespaces or comments",XturtlePackage.Literals.RESOURCE_REF__REF,"qnameWS");
}
}
@Check(CheckType.EXPENSIVE)
public void checkNoWhitespaceInQName(QNameDef qnameRef) {
if(containsWhitespace(qnameRef)){
error("qname must not contain whitespaces or comments",XturtlePackage.Literals.QNAME_DEF__ID,"qnameWS");
}
}
@Check
public void checkEmptyPrefixDefined(QNameDef def) {
if(def.getPrefix()==null && service.getQualifiedName(def)==null){
error("no @prefix-Definition up to this point", XturtlePackage.Literals.QNAME_DEF__PREFIX,
UNKNOWN_PREFIX,"");
}
}
@Check
public void checkEmptyPrefixDefined(QNameRef ref) {
if(ref.getPrefix()==null && service.getQualifiedName(ref)==null){
error("no @prefix-Definition up to this point", XturtlePackage.Literals.QNAME_REF__PREFIX,"");
}
}
//check prefix definition is in line with prefix.cc
@Check
public void checkPrefixCC(PrefixId def) {
Severity severity=levels.getNamespaceMismatchLevel();
if(severity!=null){
if(def.getId()!=null && prefixes.isKnownPrefix(def.getId())){
List<String> expectedNs=prefixes.getUris(def.getId());
if(!expectedNs.contains(service.getUriString(def))){
createError(severity, "Namespace <"+expectedNs+"> is recommended by prefix.cc", XturtlePackage.Literals.PREFIX_ID__ID);
}
}
}
severity=levels.getPrefixMismatchLevel();
if(severity!=null){
String uri = service.getUriString(def);
if(uri!=null && prefixes.isKnownNameSpace(uri)){
List<String> expectedPrefixes=prefixes.getPrefixes(uri);
if(def.getId()!=null && !expectedPrefixes.contains(def.getId())){
createError(severity,"Prefix '"+expectedPrefixes.get(0)+"' is recommended by prefix.cc", XturtlePackage.Literals.PREFIX_ID__ID);
}
}
}
}
@Check
public void checkBlankNodePrefix(PrefixId def) {
if("_".equals(def.getId())){
error("illegal prefix definition", XturtlePackage.Literals.PREFIX_ID__ID, "blank_prefix");
}
}
@Check
public void checkBlankNodeSubject(PredicateObjectList predObj) {
Predicate predicate = predObj.getVerb();
if(predicate instanceof QNameRef){
QNameRef ref = (QNameRef) predicate;
boolean isBlankPrefix=ref.getPrefix().getId()==null && ref.getPrefix().getUri()==null;
if(isBlankPrefix){
error("blank node reference cannot be a subject", predicate, XturtlePackage.Literals.QNAME_REF__PREFIX);
}
}
}
@Check
public void checkUnusedPrefix(PrefixId def) {
Severity s=levels.getUnusedPrefixLevel();
if(s!=null){
if(def.getId()!=null){
Collection<Setting> candidates = EcoreUtil.UsageCrossReferencer.find(def, def.eResource());
if(candidates.size()==0){
createError(s, "unused prefix", XturtlePackage.Literals.PREFIX_ID__ID);
}
}else{
//TODO currently no validation for unused empty prefixes
}
}
}
@Check
public void checkDuplicatePrefixInDirectives(Directives directives){
Map<String,PrefixId> firstOccurence=new HashMap<String,PrefixId>();
Set<String> duplicatePrefixes=new HashSet<String>();
for (PrefixId prefixId : EcoreUtil2.typeSelect(directives.getDirective(), PrefixId.class)) {
String prefix=prefixId.getId();
if(firstOccurence.containsKey(prefix)){
duplicatePrefixes.add(prefix);
error("duplicate prefix id", prefixId, XturtlePackage.Literals.PREFIX_ID__ID,-1);
}else{
firstOccurence.put(prefix, prefixId);
}
}
for (String string : duplicatePrefixes) {
error("duplicate prefix id",firstOccurence.get(string), XturtlePackage.Literals.PREFIX_ID__ID,-1);
}
}
@Check
public void checkXSDType(StringLiteral literal){
Severity level=levels.getXsdTypeLevel();
if(level!=null && literal.getType()!=null){
QualifiedName uri = service.getQualifiedName(literal.getType());
Optional<String> errorMessage = XsdTypeValidator.getXsdError(literal.getValue(), uri);
if(errorMessage.isPresent()){
createError(level, errorMessage.get(), XturtlePackage.Literals.LITERAL__VALUE);
}
}
}
@Check
public void preventRdfListPropertySubject(Resource subject){
String subjectUri = service.getUriString(subject);
if(linkingErrorExceptions.matchesRdfListProperty(subjectUri)){
EStructuralFeature feature=(subject instanceof UriDef)?XturtlePackage.Literals.URI_DEF__URI:XturtlePackage.Literals.QNAME_DEF__ID;
error("rdf list property not allowed as subject", feature);
}
}
protected void createError(Severity s, String errorMEssage, EStructuralFeature feature){
switch (s) {
case ERROR:
error(errorMEssage, feature);
break;
case WARNING:
warning(errorMEssage, feature);
break;
case INFO:
info(errorMEssage, feature);
break;
default:
break;
}
}
protected TurtleResourceService getService() {
return service;
}
}