-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
745 Проверка не локализированной строки
- Loading branch information
Showing
2 changed files
with
366 additions
and
0 deletions.
There are no files selected for viewing
198 changes: 198 additions & 0 deletions
198
bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/LocalizableRegistry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
||
} | ||
|
||
} |
168 changes: 168 additions & 0 deletions
168
...com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/NstrLocalizableStringCheck.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
} |