Skip to content

Commit 551fa97

Browse files
committed
745 Проверка не локализированной строки
1 parent 0bc1f9d commit 551fa97

File tree

2 files changed

+366
-0
lines changed

2 files changed

+366
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/**
2+
*
3+
*/
4+
package com.e1c.v8codestyle.bsl.check;
5+
6+
import java.util.Collection;
7+
import java.util.HashMap;
8+
import java.util.Map;
9+
import java.util.Set;
10+
11+
import org.eclipse.xtext.util.Pair;
12+
import org.eclipse.xtext.util.Tuples;
13+
14+
import com._1c.g5.v8.dt.lcore.util.CaseInsensitiveString;
15+
import com._1c.g5.v8.dt.platform.IEObjectTypeNames;
16+
import com.google.inject.Singleton;
17+
18+
/**
19+
* @author Dmitriy Marmyshev
20+
*
21+
*/
22+
@Singleton
23+
public class LocalizableRegistry
24+
{
25+
26+
private Map<CaseInsensitiveString, Collection<Integer>> staticInvocation;
27+
28+
private Map<Pair<CaseInsensitiveString, Integer>, Collection<String>> dynamicInvocation;
29+
30+
private Map<CaseInsensitiveString, Collection<String>> dynamicProperties;
31+
32+
private volatile boolean initialized;
33+
34+
public Collection<Integer> getStaticInvocationParameters(String methodName)
35+
{
36+
if (methodName == null)
37+
{
38+
return Set.of();
39+
}
40+
checkInit();
41+
42+
return staticInvocation.getOrDefault(new CaseInsensitiveString(methodName), Set.of());
43+
}
44+
45+
public Collection<String> getDynamicTypesForMethod(String methodName, int index)
46+
{
47+
if (methodName == null || index < 0)
48+
{
49+
return Set.of();
50+
}
51+
52+
checkInit();
53+
54+
return dynamicInvocation.getOrDefault(Tuples.create(new CaseInsensitiveString(methodName), index), Set.of());
55+
}
56+
57+
public Collection<String> getDynamicTypesForProperty(String propertyName)
58+
{
59+
if (propertyName == null)
60+
{
61+
return Set.of();
62+
}
63+
64+
checkInit();
65+
66+
return dynamicProperties.getOrDefault(new CaseInsensitiveString(propertyName), Set.of());
67+
}
68+
69+
private void checkInit()
70+
{
71+
if (initialized)
72+
{
73+
return;
74+
}
75+
76+
init();
77+
}
78+
79+
private synchronized void init()
80+
{
81+
if (initialized)
82+
{
83+
return;
84+
}
85+
86+
initStaticInvocation();
87+
initDynamicInvocation();
88+
initDynamicProperties();
89+
90+
initialized = true;
91+
}
92+
93+
private void initStaticInvocation()
94+
{
95+
// Global context method name and index of localizable string parameter
96+
Map<CaseInsensitiveString, Collection<Integer>> invocations = new HashMap<>();
97+
invocations.put(new CaseInsensitiveString("ПоказатьВопрос"), Set.of(1, 5)); //$NON-NLS-1$
98+
invocations.put(new CaseInsensitiveString("ShowQueryBox"), Set.of(1, 5)); //$NON-NLS-1$
99+
invocations.put(new CaseInsensitiveString("Вопрос"), Set.of(0, 4)); //$NON-NLS-1$
100+
invocations.put(new CaseInsensitiveString("DoQueryBox"), Set.of(0, 4)); //$NON-NLS-1$
101+
invocations.put(new CaseInsensitiveString("Сообщить"), Set.of(0)); //$NON-NLS-1$
102+
invocations.put(new CaseInsensitiveString("Message"), Set.of(0)); //$NON-NLS-1$
103+
invocations.put(new CaseInsensitiveString("Состояние"), Set.of(0, 2)); //$NON-NLS-1$
104+
invocations.put(new CaseInsensitiveString("Status"), Set.of(0, 2)); //$NON-NLS-1$
105+
invocations.put(new CaseInsensitiveString("ПоказатьОповещениеПользователя"), Set.of(0, 2)); //$NON-NLS-1$
106+
invocations.put(new CaseInsensitiveString("ShowUserNotification"), Set.of(0, 2)); //$NON-NLS-1$
107+
invocations.put(new CaseInsensitiveString("ПоказатьПредупреждение"), Set.of(1, 3)); //$NON-NLS-1$
108+
invocations.put(new CaseInsensitiveString("ShowMessageBox"), Set.of(1, 3)); //$NON-NLS-1$
109+
invocations.put(new CaseInsensitiveString("Предупреждение"), Set.of(0, 2)); //$NON-NLS-1$
110+
invocations.put(new CaseInsensitiveString("DoMessageBox"), Set.of(0, 2)); //$NON-NLS-1$
111+
112+
staticInvocation = Map.copyOf(invocations);
113+
}
114+
115+
private void initDynamicInvocation()
116+
{
117+
//@formatter:off
118+
119+
// Object's method name and index of localizable string parameter, and collection of object's type names
120+
dynamicInvocation = Map.of(
121+
Tuples.create(new CaseInsensitiveString("Добавить"), 1), Set.of(IEObjectTypeNames.VALUE_LIST), //$NON-NLS-1$
122+
Tuples.create(new CaseInsensitiveString("Add"), 1), Set.of(IEObjectTypeNames.VALUE_LIST) //$NON-NLS-1$
123+
);
124+
125+
//@formatter:on
126+
127+
}
128+
129+
private void initDynamicProperties()
130+
{
131+
//@formatter:off
132+
133+
// Types that contains property Title
134+
Set<String> titleTypes = Set.of(
135+
IEObjectTypeNames.FORM_FIELD,
136+
IEObjectTypeNames.FORM_GROUP,
137+
IEObjectTypeNames.FORM_TABLE,
138+
IEObjectTypeNames.FORM_DECORATION,
139+
IEObjectTypeNames.FORM_COMMAND,
140+
"FormAttribute", //$NON-NLS-1$
141+
"FormItemAddition", //$NON-NLS-1$
142+
IEObjectTypeNames.FORM_BUTTON,
143+
IEObjectTypeNames.CLIENT_APPLICATION_FORM,
144+
"ConditionalAppearenceItem", //$NON-NLS-1$
145+
"AppearenceSettingItem", //$NON-NLS-1$
146+
"CollaborationSystemConversation", //$NON-NLS-1$
147+
"DeliverableNotification", //$NON-NLS-1$
148+
"RepresentableDocumentBatch", //$NON-NLS-1$
149+
"HTMLDocument", //$NON-NLS-1$
150+
"ValueTableColumn", //$NON-NLS-1$
151+
"ValueTreeColumn", //$NON-NLS-1$
152+
"DataCompositionAreaTemplateValueCollectionHeaderCell", //$NON-NLS-1$
153+
IEObjectTypeNames.DATA_COMPOSITION_USER_FIELD_EXPRESSION,
154+
IEObjectTypeNames.DATA_COMPOSITION_USER_FIELD_CASE,
155+
IEObjectTypeNames.DATA_COMPOSITION_SELECTED_FIELD_GROUP,
156+
IEObjectTypeNames.DATA_COMPOSITION_SELECTED_FIELD,
157+
IEObjectTypeNames.DATA_COMPOSITION_FILTER_AVAILABLE_FIELD,
158+
"NestedDataCompositionSchema", //$NON-NLS-1$
159+
"DataCompositionSchemaParameter", //$NON-NLS-1$
160+
"DataCompositionSchemaNestedDataSet", //$NON-NLS-1$
161+
"DataCompositionSchemaDataSetFieldFolder", //$NON-NLS-1$
162+
"DataCompositionSchemaDataSetField", //$NON-NLS-1$
163+
"DataCompositionSchemaCalculatedField", //$NON-NLS-1$
164+
IEObjectTypeNames.DATA_ANALYSIS_PARAMETERS,
165+
"GanttChartPlotArea", //$NON-NLS-1$
166+
"FileDialog" //$NON-NLS-1$
167+
);
168+
169+
// Types that contains property ToolTip
170+
// TODO add all types with tooltip
171+
Set<String> toolTipTypes = Set.of(
172+
IEObjectTypeNames.FORM_FIELD,
173+
IEObjectTypeNames.FORM_GROUP,
174+
IEObjectTypeNames.FORM_TABLE,
175+
IEObjectTypeNames.FORM_DECORATION,
176+
IEObjectTypeNames.FORM_COMMAND,
177+
"FormItemAddition", //$NON-NLS-1$
178+
"DateAppearence" //$NON-NLS-1$
179+
);
180+
181+
// TODO add types of graphical scheme with Description
182+
183+
// TODO add types of DCS with Presentation
184+
185+
// Localizable property name, and collection of types
186+
dynamicProperties = Map.of(
187+
new CaseInsensitiveString("Подсказка"), toolTipTypes, //$NON-NLS-1$
188+
new CaseInsensitiveString("ToolTip"), toolTipTypes, //$NON-NLS-1$
189+
new CaseInsensitiveString("ПодсказкаВвода"), Set.of("FormFieldExtenstionForTextBox"), //$NON-NLS-1$
190+
new CaseInsensitiveString("InputHint"), Set.of("FormFieldExtenstionForTextBox"), //$NON-NLS-1$
191+
new CaseInsensitiveString("Заголовок"), titleTypes, //$NON-NLS-1$
192+
new CaseInsensitiveString("Title"), titleTypes //$NON-NLS-1$
193+
);
194+
//@formatter:on
195+
196+
}
197+
198+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*******************************************************************************
2+
* Copyright (C) 2022, 1C-Soft LLC and others.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* 1C-Soft LLC - initial API and implementation
12+
*******************************************************************************/
13+
/**
14+
*
15+
*/
16+
package com.e1c.v8codestyle.bsl.check;
17+
18+
import static com._1c.g5.v8.dt.bsl.model.BslPackage.Literals.STRING_LITERAL;
19+
import static com._1c.g5.v8.dt.bsl.model.BslPackage.Literals.STRING_LITERAL__LINES;
20+
21+
import java.util.Collection;
22+
import java.util.List;
23+
import java.util.Objects;
24+
25+
import org.eclipse.core.runtime.IProgressMonitor;
26+
import org.eclipse.emf.ecore.EObject;
27+
import org.eclipse.xtext.EcoreUtil2;
28+
29+
import com._1c.g5.v8.dt.bsl.model.DynamicFeatureAccess;
30+
import com._1c.g5.v8.dt.bsl.model.FeatureAccess;
31+
import com._1c.g5.v8.dt.bsl.model.Invocation;
32+
import com._1c.g5.v8.dt.bsl.model.SimpleStatement;
33+
import com._1c.g5.v8.dt.bsl.model.StaticFeatureAccess;
34+
import com._1c.g5.v8.dt.bsl.model.StringLiteral;
35+
import com._1c.g5.v8.dt.bsl.resource.TypesComputer;
36+
import com._1c.g5.v8.dt.common.StringUtils;
37+
import com._1c.g5.v8.dt.mcore.Environmental;
38+
import com._1c.g5.v8.dt.mcore.TypeItem;
39+
import com._1c.g5.v8.dt.mcore.util.McoreUtil;
40+
import com.e1c.g5.v8.dt.check.CheckComplexity;
41+
import com.e1c.g5.v8.dt.check.ICheckParameters;
42+
import com.e1c.g5.v8.dt.check.components.BasicCheck;
43+
import com.e1c.g5.v8.dt.check.settings.IssueSeverity;
44+
import com.e1c.g5.v8.dt.check.settings.IssueType;
45+
import com.e1c.v8codestyle.check.CommonSenseCheckExtension;
46+
import com.e1c.v8codestyle.internal.bsl.BslPlugin;
47+
import com.google.inject.Inject;
48+
49+
/**
50+
* @author Dmitriy Marmyshev
51+
*
52+
*/
53+
public class NstrLocalizableStringCheck
54+
extends BasicCheck
55+
{
56+
private static final String CHECK_ID = "bsl-localizable-string"; //$NON-NLS-1$
57+
58+
private final TypesComputer typesComputer;
59+
60+
private final LocalizableRegistry localizableRegistry;
61+
62+
@Inject
63+
public NstrLocalizableStringCheck(TypesComputer typesComputer, LocalizableRegistry localizableRegistry)
64+
{
65+
this.typesComputer = typesComputer;
66+
this.localizableRegistry = localizableRegistry;
67+
}
68+
69+
@Override
70+
public String getCheckId()
71+
{
72+
return CHECK_ID;
73+
}
74+
75+
@Override
76+
protected void configureCheck(CheckConfigurer builder)
77+
{
78+
builder.title("String literal should be localizable with NStr")
79+
.description("String literal should be localizable with NStr")
80+
.complexity(CheckComplexity.NORMAL)
81+
.severity(IssueSeverity.MINOR)
82+
.issueType(IssueType.UI_STYLE)
83+
.extension(new CommonSenseCheckExtension(getCheckId(), BslPlugin.PLUGIN_ID))
84+
.module()
85+
.checkedObjectType(STRING_LITERAL);
86+
87+
}
88+
89+
@Override
90+
protected void check(Object object, ResultAcceptor resultAceptor, ICheckParameters parameters,
91+
IProgressMonitor monitor)
92+
{
93+
StringLiteral literal = (StringLiteral)object;
94+
95+
if (literal.getLines().size() == 1 && StringUtils.isBlank(literal.lines(true).get(0)))
96+
{
97+
// skip empty lines
98+
return;
99+
}
100+
101+
EObject parent = literal.eContainer();
102+
103+
if (parent instanceof Invocation && isLocalizableParameter((Invocation)parent, literal, monitor)
104+
|| parent instanceof SimpleStatement && ((SimpleStatement)parent).getLeft() instanceof DynamicFeatureAccess
105+
&& !monitor.isCanceled()
106+
&& isLocalizableProperty((DynamicFeatureAccess)((SimpleStatement)parent).getLeft(), monitor))
107+
{
108+
resultAceptor.addIssue("Localizable string should be in NStr()", STRING_LITERAL__LINES);
109+
}
110+
111+
}
112+
113+
private boolean isLocalizableParameter(Invocation inv, EObject parameter, IProgressMonitor monitor)
114+
{
115+
FeatureAccess method = inv.getMethodAccess();
116+
if (StringUtils.isBlank(method.getName()))
117+
{
118+
return false;
119+
}
120+
121+
if (method instanceof StaticFeatureAccess && !monitor.isCanceled())
122+
{
123+
Collection<Integer> params = localizableRegistry.getStaticInvocationParameters(method.getName());
124+
if (!params.isEmpty())
125+
{
126+
int index = inv.getParams().indexOf(parameter);
127+
return params.contains(index);
128+
}
129+
}
130+
else if (method instanceof DynamicFeatureAccess && !monitor.isCanceled())
131+
{
132+
int index = inv.getParams().indexOf(parameter);
133+
DynamicFeatureAccess dfa = (DynamicFeatureAccess)method;
134+
Collection<String> typeNames = localizableRegistry.getDynamicTypesForMethod(dfa.getName(), index);
135+
if (!typeNames.isEmpty() && !monitor.isCanceled())
136+
{
137+
Environmental env = EcoreUtil2.getContainerOfType(dfa, Environmental.class);
138+
List<TypeItem> types = typesComputer.computeTypes(dfa.getSource(), env.environments());
139+
return !types.isEmpty() && types.stream()
140+
.map(McoreUtil::getTypeName)
141+
.filter(Objects::nonNull)
142+
.anyMatch(typeNames::contains);
143+
}
144+
}
145+
return false;
146+
}
147+
148+
private boolean isLocalizableProperty(DynamicFeatureAccess property, IProgressMonitor monitor)
149+
{
150+
String name = property.getName();
151+
if (StringUtils.isBlank(name))
152+
{
153+
return false;
154+
}
155+
Collection<String> typeNames = localizableRegistry.getDynamicTypesForProperty(name);
156+
157+
if (!typeNames.isEmpty() && !monitor.isCanceled())
158+
{
159+
Environmental env = EcoreUtil2.getContainerOfType(property, Environmental.class);
160+
List<TypeItem> types = typesComputer.computeTypes(property.getSource(), env.environments());
161+
return !types.isEmpty()
162+
&& types.stream().map(McoreUtil::getTypeName).filter(Objects::nonNull).anyMatch(typeNames::contains);
163+
}
164+
165+
return false;
166+
}
167+
168+
}

0 commit comments

Comments
 (0)