/** * Copyright 2004-2016 Riccardo Solmi. All rights reserved. * This file is part of the Whole Platform. * * The Whole Platform is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Whole Platform is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>. */ package org.whole.lang.models.visitors; import java.util.HashSet; import java.util.Set; import org.whole.lang.commons.reflect.CommonsEntityDescriptorEnum; import org.whole.lang.iterators.AbstractPatternFilterIterator; import org.whole.lang.iterators.IEntityIterator; import org.whole.lang.iterators.IteratorFactory; import org.whole.lang.matchers.Matcher; import org.whole.lang.model.IEntity; import org.whole.lang.models.model.ComponentModifier; import org.whole.lang.models.model.ComponentModifierEnum; import org.whole.lang.models.model.CompositeEntity; import org.whole.lang.models.model.DataEntity; import org.whole.lang.models.model.DataType; import org.whole.lang.models.model.EnumEntity; import org.whole.lang.models.model.Feature; import org.whole.lang.models.model.FeatureModifier; import org.whole.lang.models.model.FeatureModifierEnum; import org.whole.lang.models.model.MapEntity; import org.whole.lang.models.model.Model; import org.whole.lang.models.model.ModelDeclaration; import org.whole.lang.models.model.ModelDeclarations; import org.whole.lang.models.model.SimpleEntity; import org.whole.lang.models.model.SimpleName; import org.whole.lang.models.model.Types; import org.whole.lang.models.reflect.ModelsEntityDescriptorEnum; import org.whole.lang.models.reflect.ModelsFeatureDescriptorEnum; import org.whole.lang.models.util.ModelInfo; import org.whole.lang.operations.IDecorationManager; import org.whole.lang.operations.ValidatorOperation; import org.whole.lang.util.EntityUtils; import org.whole.lang.util.StringUtils; /** * @author Riccardo Solmi */ public class ModelsValidatorVisitor extends ModelsTraverseAllVisitor { private ModelInfo modelInfo; private IEntity currentEntity; public ModelsValidatorVisitor() { } private IDecorationManager decorationManager; public IDecorationManager getDecorationManager() { if (decorationManager == null) decorationManager = ((ValidatorOperation) getOperation()).getDecorationManager(); return decorationManager; } @Override public void visit(Model entity) { modelInfo = new ModelInfo(entity); super.visit(entity); } @Override public void visit(ModelDeclarations entity) { for (int i = 0; i < entity.wSize(); i++) { ModelDeclaration decl = (ModelDeclaration) entity.wGet(i); currentEntity = decl; decl.accept(this); } } @Override public void visit(SimpleEntity entity) { validateEntityDeclarationName(entity.getName()); super.visit(entity); } @Override public void visit(CompositeEntity entity) { validateEntityDeclarationName(entity.getName()); validateDeclaredType(entity.getComponentType()); super.visit(entity); } @Override public void visit(MapEntity entity) { validateEntityDeclarationName(entity.getName()); validateDeclaredType(entity.getKeyType()); validateDeclaredType(entity.getValueType()); super.visit(entity); } @Override public void visit(DataEntity entity) { validateEntityDeclarationName(entity.getName()); validateNoResolver(entity.getDataType()); super.visit(entity); } @Override public void visit(EnumEntity entity) { validateEntityDeclarationName(entity.getName()); if (entity.getValues().wIsEmpty()) getDecorationManager().addError( entity.getValues(), "At least one enum value must be defined.", location(entity)); super.visit(entity); } @Override public void visit(Feature entity) { validateFeatureDeclarationName(entity.getName()); validateDeclaredType(entity.getType()); super.visit(entity); } @Override public void visit(DataType entity) { if (StringUtils.isWrapper(entity.getValue())) getDecorationManager().addWarning( entity, "A data entity is already a wrapper.", location(entity)); } @Override public void visit(ComponentModifier entity) { switch (entity.wEnumValue().getOrdinal()) { case ComponentModifierEnum.derived_ord: case ComponentModifierEnum.shared_ord: getDecorationManager().addWarning( entity, "Unsupported component modifier: "+entity.wEnumValue().getName(), location(entity)); } } @Override public void visit(FeatureModifier entity) { switch (entity.wEnumValue().getOrdinal()) { case FeatureModifierEnum.derived_ord: case FeatureModifierEnum.shared_ord: getDecorationManager().addWarning( entity, "Unsupported feature modifier: "+entity.wEnumValue().getName(), location(entity)); } } @Override public void visit(Types entity) { AbstractPatternFilterIterator<SimpleName> i = IteratorFactory.<SimpleName>childMatcherIterator() .withPattern(ModelsEntityDescriptorEnum.SimpleName); i.reset(entity); for (SimpleName t : i) { String typeName = t.getValue(); if (modelInfo.undefinedTypes.contains(typeName)) getDecorationManager().addWarning( t, "Reference to the undeclared type: "+typeName, location(entity)); if (!modelInfo.simpleEntityTypes.contains(typeName) && !modelInfo.markerTypes.contains(typeName) && !modelInfo.undefinedTypes.contains(typeName)) getDecorationManager().addError( t, "Only Simple entities can be extended", location(entity)); //TODO in entity visit if (currentEntity != null && !currentEntity.wGetEntityDescriptor().equals(ModelsEntityDescriptorEnum.SimpleEntity)) { for (String name : modelInfo.allSuperTypes(typeName)) if (!modelInfo.markerTypes.contains(name) && !modelInfo.undefinedTypes.contains(name)) getDecorationManager().addError( t, "The supertypes of a "+currentEntity.wGetEntityDescriptor().getName()+" must be marker types (SimpleEntities without features)", location(entity)); } isSimpleNameEmpty(t, "Reference type name is empty."); } } protected boolean isSimpleNameEmpty(SimpleName entity, String errorMessage) { boolean isEmpty = entity.getValue().length() == 0; if (isEmpty) getDecorationManager().addError( entity, errorMessage, location(entity)); return isEmpty; } private Set<String> declaredEntityNames = new HashSet<String>(); protected void validateEntityDeclarationName(IEntity entity) { if (!Matcher.match(ModelsEntityDescriptorEnum.SimpleName, entity)) { getDecorationManager().addError( entity, "The entity name is mandatory.", location(entity)); return; } else if (isSimpleNameEmpty((SimpleName) entity, "Entity name is empty.")) return; String name = entity.wStringValue(); if (!declaredEntityNames.add(name)) getDecorationManager().addError( entity, "Duplicate entity name: "+name, location(entity)); if (StringUtils.isAmbiguous(name)) getDecorationManager().addWarning( entity, "The entity name is ambiguous (see java.lang): "+name, location(entity)); if (name.length() == 0) getDecorationManager().addError( entity, "The entity name is empty", location(entity)); else if (Character.isLowerCase(name.charAt(0))) getDecorationManager().addWarning( entity, "The entity name will be replaced by: "+StringUtils.toUpperCap(name), location(entity)); } protected void validateFeatureDeclarationName(IEntity entity) { if (!Matcher.match(ModelsEntityDescriptorEnum.SimpleName, entity)) { getDecorationManager().addError( entity, "The feature name is mandatory.", location(entity)); return; } String name = entity.wStringValue(); if (StringUtils.isJavaKeyword(name)) getDecorationManager().addWarning( entity, "The feature name will be replaced by: _"+name, location(entity)); if (name.length() == 0) getDecorationManager().addError( entity, "The feature name is empty", location(entity)); else if (Character.isUpperCase(name.charAt(0))) getDecorationManager().addWarning( entity, "The feature name will be replaced by: "+StringUtils.toLowerCap(name), location(entity)); } protected boolean validateNoResolver(IEntity entity) { if (EntityUtils.isResolver(entity) && !(CommonsEntityDescriptorEnum.StageUpFragment.equals(entity.wGetModel().getFragment().wGetEntityDescriptor()))) { getDecorationManager().addError( entity, "Missed mandatory feature: " + entity.wGetParent().wGetFeatureDescriptor(entity).getName(), location(entity)); return false; } return true; } protected void validateDeclaredType(IEntity entity) { if (!validateNoResolver(entity) || !Matcher.match(ModelsEntityDescriptorEnum.SimpleName, entity)) return; String typeName = entity.wStringValue(); if (modelInfo.undefinedTypes.contains(typeName)) getDecorationManager().addWarning( entity, "Reference to the undeclared type: "+typeName, location(entity)); if (typeName.length() == 0) getDecorationManager().addError( entity, "The feature type is empty", location(entity)); } protected String location(IEntity entity) { String result = ""; IEntityIterator<IEntity> i = IteratorFactory.ancestorReverseIterator(); i.reset(entity); for (IEntity e : i) { if (e.wContains(ModelsFeatureDescriptorEnum.name) && !e.wGetEntityDescriptor().equals(ModelsEntityDescriptorEnum.Model)) { if (result.length() > 0) result += "."; result += e.wGet(ModelsFeatureDescriptorEnum.name).toString(); } } return result; } }