Skip to content

Commit

Permalink
745 Проверка не локализированной строки
Browse files Browse the repository at this point in the history
  • Loading branch information
marmyshev committed Sep 16, 2022
1 parent 0bc1f9d commit 551fa97
Show file tree
Hide file tree
Showing 2 changed files with 366 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/**
*
*/
package com.e1c.v8codestyle.bsl.check;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;

import com._1c.g5.v8.dt.lcore.util.CaseInsensitiveString;
import com._1c.g5.v8.dt.platform.IEObjectTypeNames;
import com.google.inject.Singleton;

/**
* @author Dmitriy Marmyshev
*
*/
@Singleton
public class LocalizableRegistry
{

private Map<CaseInsensitiveString, Collection<Integer>> staticInvocation;

private Map<Pair<CaseInsensitiveString, Integer>, Collection<String>> dynamicInvocation;

private Map<CaseInsensitiveString, Collection<String>> dynamicProperties;

private volatile boolean initialized;

public Collection<Integer> getStaticInvocationParameters(String methodName)
{
if (methodName == null)
{
return Set.of();
}
checkInit();

return staticInvocation.getOrDefault(new CaseInsensitiveString(methodName), Set.of());
}

public Collection<String> getDynamicTypesForMethod(String methodName, int index)
{
if (methodName == null || index < 0)
{
return Set.of();
}

checkInit();

return dynamicInvocation.getOrDefault(Tuples.create(new CaseInsensitiveString(methodName), index), Set.of());
}

public Collection<String> getDynamicTypesForProperty(String propertyName)
{
if (propertyName == null)
{
return Set.of();
}

checkInit();

return dynamicProperties.getOrDefault(new CaseInsensitiveString(propertyName), Set.of());
}

private void checkInit()
{
if (initialized)
{
return;
}

init();
}

private synchronized void init()
{
if (initialized)
{
return;
}

initStaticInvocation();
initDynamicInvocation();
initDynamicProperties();

initialized = true;
}

private void initStaticInvocation()
{
// Global context method name and index of localizable string parameter
Map<CaseInsensitiveString, Collection<Integer>> invocations = new HashMap<>();
invocations.put(new CaseInsensitiveString("ПоказатьВопрос"), Set.of(1, 5)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("ShowQueryBox"), Set.of(1, 5)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("Вопрос"), Set.of(0, 4)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("DoQueryBox"), Set.of(0, 4)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("Сообщить"), Set.of(0)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("Message"), Set.of(0)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("Состояние"), Set.of(0, 2)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("Status"), Set.of(0, 2)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("ПоказатьОповещениеПользователя"), Set.of(0, 2)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("ShowUserNotification"), Set.of(0, 2)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("ПоказатьПредупреждение"), Set.of(1, 3)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("ShowMessageBox"), Set.of(1, 3)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("Предупреждение"), Set.of(0, 2)); //$NON-NLS-1$
invocations.put(new CaseInsensitiveString("DoMessageBox"), Set.of(0, 2)); //$NON-NLS-1$

staticInvocation = Map.copyOf(invocations);
}

private void initDynamicInvocation()
{
//@formatter:off

// Object's method name and index of localizable string parameter, and collection of object's type names
dynamicInvocation = Map.of(
Tuples.create(new CaseInsensitiveString("Добавить"), 1), Set.of(IEObjectTypeNames.VALUE_LIST), //$NON-NLS-1$
Tuples.create(new CaseInsensitiveString("Add"), 1), Set.of(IEObjectTypeNames.VALUE_LIST) //$NON-NLS-1$
);

//@formatter:on

}

private void initDynamicProperties()
{
//@formatter:off

// Types that contains property Title
Set<String> titleTypes = Set.of(
IEObjectTypeNames.FORM_FIELD,
IEObjectTypeNames.FORM_GROUP,
IEObjectTypeNames.FORM_TABLE,
IEObjectTypeNames.FORM_DECORATION,
IEObjectTypeNames.FORM_COMMAND,
"FormAttribute", //$NON-NLS-1$
"FormItemAddition", //$NON-NLS-1$
IEObjectTypeNames.FORM_BUTTON,
IEObjectTypeNames.CLIENT_APPLICATION_FORM,
"ConditionalAppearenceItem", //$NON-NLS-1$
"AppearenceSettingItem", //$NON-NLS-1$
"CollaborationSystemConversation", //$NON-NLS-1$
"DeliverableNotification", //$NON-NLS-1$
"RepresentableDocumentBatch", //$NON-NLS-1$
"HTMLDocument", //$NON-NLS-1$
"ValueTableColumn", //$NON-NLS-1$
"ValueTreeColumn", //$NON-NLS-1$
"DataCompositionAreaTemplateValueCollectionHeaderCell", //$NON-NLS-1$
IEObjectTypeNames.DATA_COMPOSITION_USER_FIELD_EXPRESSION,
IEObjectTypeNames.DATA_COMPOSITION_USER_FIELD_CASE,
IEObjectTypeNames.DATA_COMPOSITION_SELECTED_FIELD_GROUP,
IEObjectTypeNames.DATA_COMPOSITION_SELECTED_FIELD,
IEObjectTypeNames.DATA_COMPOSITION_FILTER_AVAILABLE_FIELD,
"NestedDataCompositionSchema", //$NON-NLS-1$
"DataCompositionSchemaParameter", //$NON-NLS-1$
"DataCompositionSchemaNestedDataSet", //$NON-NLS-1$
"DataCompositionSchemaDataSetFieldFolder", //$NON-NLS-1$
"DataCompositionSchemaDataSetField", //$NON-NLS-1$
"DataCompositionSchemaCalculatedField", //$NON-NLS-1$
IEObjectTypeNames.DATA_ANALYSIS_PARAMETERS,
"GanttChartPlotArea", //$NON-NLS-1$
"FileDialog" //$NON-NLS-1$
);

// Types that contains property ToolTip
// TODO add all types with tooltip
Set<String> toolTipTypes = Set.of(
IEObjectTypeNames.FORM_FIELD,
IEObjectTypeNames.FORM_GROUP,
IEObjectTypeNames.FORM_TABLE,
IEObjectTypeNames.FORM_DECORATION,
IEObjectTypeNames.FORM_COMMAND,
"FormItemAddition", //$NON-NLS-1$
"DateAppearence" //$NON-NLS-1$
);

// TODO add types of graphical scheme with Description

// TODO add types of DCS with Presentation

// Localizable property name, and collection of types
dynamicProperties = Map.of(
new CaseInsensitiveString("Подсказка"), toolTipTypes, //$NON-NLS-1$
new CaseInsensitiveString("ToolTip"), toolTipTypes, //$NON-NLS-1$
new CaseInsensitiveString("ПодсказкаВвода"), Set.of("FormFieldExtenstionForTextBox"), //$NON-NLS-1$
new CaseInsensitiveString("InputHint"), Set.of("FormFieldExtenstionForTextBox"), //$NON-NLS-1$
new CaseInsensitiveString("Заголовок"), titleTypes, //$NON-NLS-1$
new CaseInsensitiveString("Title"), titleTypes //$NON-NLS-1$
);
//@formatter:on

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*******************************************************************************
* Copyright (C) 2022, 1C-Soft LLC and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* 1C-Soft LLC - initial API and implementation
*******************************************************************************/
/**
*
*/
package com.e1c.v8codestyle.bsl.check;

import static com._1c.g5.v8.dt.bsl.model.BslPackage.Literals.STRING_LITERAL;
import static com._1c.g5.v8.dt.bsl.model.BslPackage.Literals.STRING_LITERAL__LINES;

import java.util.Collection;
import java.util.List;
import java.util.Objects;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.EcoreUtil2;

import com._1c.g5.v8.dt.bsl.model.DynamicFeatureAccess;
import com._1c.g5.v8.dt.bsl.model.FeatureAccess;
import com._1c.g5.v8.dt.bsl.model.Invocation;
import com._1c.g5.v8.dt.bsl.model.SimpleStatement;
import com._1c.g5.v8.dt.bsl.model.StaticFeatureAccess;
import com._1c.g5.v8.dt.bsl.model.StringLiteral;
import com._1c.g5.v8.dt.bsl.resource.TypesComputer;
import com._1c.g5.v8.dt.common.StringUtils;
import com._1c.g5.v8.dt.mcore.Environmental;
import com._1c.g5.v8.dt.mcore.TypeItem;
import com._1c.g5.v8.dt.mcore.util.McoreUtil;
import com.e1c.g5.v8.dt.check.CheckComplexity;
import com.e1c.g5.v8.dt.check.ICheckParameters;
import com.e1c.g5.v8.dt.check.components.BasicCheck;
import com.e1c.g5.v8.dt.check.settings.IssueSeverity;
import com.e1c.g5.v8.dt.check.settings.IssueType;
import com.e1c.v8codestyle.check.CommonSenseCheckExtension;
import com.e1c.v8codestyle.internal.bsl.BslPlugin;
import com.google.inject.Inject;

/**
* @author Dmitriy Marmyshev
*
*/
public class NstrLocalizableStringCheck
extends BasicCheck
{
private static final String CHECK_ID = "bsl-localizable-string"; //$NON-NLS-1$

private final TypesComputer typesComputer;

private final LocalizableRegistry localizableRegistry;

@Inject
public NstrLocalizableStringCheck(TypesComputer typesComputer, LocalizableRegistry localizableRegistry)
{
this.typesComputer = typesComputer;
this.localizableRegistry = localizableRegistry;
}

@Override
public String getCheckId()
{
return CHECK_ID;
}

@Override
protected void configureCheck(CheckConfigurer builder)
{
builder.title("String literal should be localizable with NStr")
.description("String literal should be localizable with NStr")
.complexity(CheckComplexity.NORMAL)
.severity(IssueSeverity.MINOR)
.issueType(IssueType.UI_STYLE)
.extension(new CommonSenseCheckExtension(getCheckId(), BslPlugin.PLUGIN_ID))
.module()
.checkedObjectType(STRING_LITERAL);

}

@Override
protected void check(Object object, ResultAcceptor resultAceptor, ICheckParameters parameters,
IProgressMonitor monitor)
{
StringLiteral literal = (StringLiteral)object;

if (literal.getLines().size() == 1 && StringUtils.isBlank(literal.lines(true).get(0)))
{
// skip empty lines
return;
}

EObject parent = literal.eContainer();

if (parent instanceof Invocation && isLocalizableParameter((Invocation)parent, literal, monitor)
|| parent instanceof SimpleStatement && ((SimpleStatement)parent).getLeft() instanceof DynamicFeatureAccess
&& !monitor.isCanceled()
&& isLocalizableProperty((DynamicFeatureAccess)((SimpleStatement)parent).getLeft(), monitor))
{
resultAceptor.addIssue("Localizable string should be in NStr()", STRING_LITERAL__LINES);
}

}

private boolean isLocalizableParameter(Invocation inv, EObject parameter, IProgressMonitor monitor)
{
FeatureAccess method = inv.getMethodAccess();
if (StringUtils.isBlank(method.getName()))
{
return false;
}

if (method instanceof StaticFeatureAccess && !monitor.isCanceled())
{
Collection<Integer> params = localizableRegistry.getStaticInvocationParameters(method.getName());
if (!params.isEmpty())
{
int index = inv.getParams().indexOf(parameter);
return params.contains(index);
}
}
else if (method instanceof DynamicFeatureAccess && !monitor.isCanceled())
{
int index = inv.getParams().indexOf(parameter);
DynamicFeatureAccess dfa = (DynamicFeatureAccess)method;
Collection<String> typeNames = localizableRegistry.getDynamicTypesForMethod(dfa.getName(), index);
if (!typeNames.isEmpty() && !monitor.isCanceled())
{
Environmental env = EcoreUtil2.getContainerOfType(dfa, Environmental.class);
List<TypeItem> types = typesComputer.computeTypes(dfa.getSource(), env.environments());
return !types.isEmpty() && types.stream()
.map(McoreUtil::getTypeName)
.filter(Objects::nonNull)
.anyMatch(typeNames::contains);
}
}
return false;
}

private boolean isLocalizableProperty(DynamicFeatureAccess property, IProgressMonitor monitor)
{
String name = property.getName();
if (StringUtils.isBlank(name))
{
return false;
}
Collection<String> typeNames = localizableRegistry.getDynamicTypesForProperty(name);

if (!typeNames.isEmpty() && !monitor.isCanceled())
{
Environmental env = EcoreUtil2.getContainerOfType(property, Environmental.class);
List<TypeItem> types = typesComputer.computeTypes(property.getSource(), env.environments());
return !types.isEmpty()
&& types.stream().map(McoreUtil::getTypeName).filter(Objects::nonNull).anyMatch(typeNames::contains);
}

return false;
}

}

0 comments on commit 551fa97

Please sign in to comment.