blob: 3b59bfb6df139f9611a4386125219ede2f64e773 [file] [log] [blame]
/*
* Copyright (c) 2011 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.eclipse.protobuf.protobuf.ProtobufPackage.Literals.ABSTRACT_OPTION__VALUE;
import static com.google.eclipse.protobuf.validation.Messages.expectedIdentifier;
import static com.google.eclipse.protobuf.validation.Messages.expectedInteger;
import static com.google.eclipse.protobuf.validation.Messages.expectedNumber;
import static com.google.eclipse.protobuf.validation.Messages.expectedPositiveNumber;
import static com.google.eclipse.protobuf.validation.Messages.expectedString;
import static com.google.eclipse.protobuf.validation.Messages.expectedTrueOrFalse;
import static com.google.eclipse.protobuf.validation.Messages.literalNotInEnum;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.validation.AbstractDeclarativeValidator;
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.EValidatorRegistrar;
import com.google.eclipse.protobuf.model.util.INodes;
import com.google.eclipse.protobuf.model.util.MessageFields;
import com.google.eclipse.protobuf.protobuf.BooleanLink;
import com.google.eclipse.protobuf.protobuf.DefaultValueFieldOption;
import com.google.eclipse.protobuf.protobuf.DoubleLink;
import com.google.eclipse.protobuf.protobuf.Enum;
import com.google.eclipse.protobuf.protobuf.HexNumberLink;
import com.google.eclipse.protobuf.protobuf.Literal;
import com.google.eclipse.protobuf.protobuf.LiteralLink;
import com.google.eclipse.protobuf.protobuf.LongLink;
import com.google.eclipse.protobuf.protobuf.MessageField;
import com.google.eclipse.protobuf.protobuf.StringLink;
import com.google.eclipse.protobuf.protobuf.Value;
import com.google.inject.Inject;
/**
* @author alruiz@google.com (Alex Ruiz)
*/
public class DataTypeValidator extends AbstractDeclarativeValidator {
public static final String EXPECTED_BOOL_ERROR = "expectedBool";
public static final String EXPECTED_STRING_ERROR = "expectedString";
@Override public void register(EValidatorRegistrar registrar) {}
@Inject private IQualifiedNameProvider fqnProvider;
@Inject private MessageFields messageFields;
@Inject private INodes nodes;
@Check public void checkValueOfDefaultTypeMatchesFieldType(DefaultValueFieldOption option) {
EObject container = option.eContainer();
if (!(container instanceof MessageField)) {
return;
}
MessageField field = (MessageField) container;
if (messageFields.isBool(field)) {
validateBool(option);
return;
}
if (messageFields.isFloatingPointNumber(field)) {
validateFloatingPointNumber(option);
return;
}
if (messageFields.isInteger(field)) {
validateInteger(option);
return;
}
if (messageFields.isUnsignedInteger(field)) {
validateUnsignedInteger(option);
return;
}
if (messageFields.isBytes(field) || messageFields.isString(field)) {
validateString(option);
return;
}
Enum anEnum = messageFields.enumTypeOf(field);
if (anEnum != null) {
validateEnumLiteral(option, anEnum);
}
}
private void validateBool(DefaultValueFieldOption option) {
Value value = option.getValue();
if (!(value instanceof BooleanLink)) {
error(expectedTrueOrFalse, option, ABSTRACT_OPTION__VALUE, EXPECTED_BOOL_ERROR);
}
}
private void validateFloatingPointNumber(DefaultValueFieldOption option) {
Value value = option.getValue();
if (!(value instanceof DoubleLink) && !isInteger(value)) {
error(expectedNumber, ABSTRACT_OPTION__VALUE);
}
}
private void validateInteger(DefaultValueFieldOption option) {
Value value = option.getValue();
if (!isInteger(value)) {
error(expectedInteger, ABSTRACT_OPTION__VALUE);
}
}
private boolean isInteger(Value value) {
return value instanceof LongLink || value instanceof HexNumberLink;
}
private void validateUnsignedInteger(DefaultValueFieldOption option) {
Value value = option.getValue();
long longValue = longValueIn(value);
if (longValue < 0) {
error(expectedPositiveNumber, ABSTRACT_OPTION__VALUE);
}
}
private long longValueIn(Value value) {
if (value instanceof LongLink) {
LongLink link = (LongLink) value;
return link.getTarget();
}
if (value instanceof HexNumberLink) {
HexNumberLink link = (HexNumberLink) value;
return link.getTarget();
}
throw new IllegalArgumentException(value + " does not belong to an integer type");
}
private void validateString(DefaultValueFieldOption option) {
Value value = option.getValue();
if (!(value instanceof StringLink)) {
error(expectedString, option, ABSTRACT_OPTION__VALUE, EXPECTED_STRING_ERROR);
}
}
private boolean validateEnumLiteral(DefaultValueFieldOption option, Enum anEnum) {
Value value = option.getValue();
if (anEnum == null) {
return false;
}
if (!(value instanceof LiteralLink)) {
error(expectedIdentifier, ABSTRACT_OPTION__VALUE);
return true;
}
Literal literal = ((LiteralLink) value).getTarget();
if (!anEnum.equals(literal.eContainer())) {
QualifiedName enumFqn = fqnProvider.getFullyQualifiedName(anEnum);
String literalName = nodes.textOf(nodeForValueFeatureIn(option));
String msg = String.format(literalNotInEnum, enumFqn, literalName);
error(msg, ABSTRACT_OPTION__VALUE);
}
return true;
}
private INode nodeForValueFeatureIn(DefaultValueFieldOption option) {
return nodes.firstNodeForFeature(option, ABSTRACT_OPTION__VALUE);
}
}