Skip to content

Commit 1fc5aaa

Browse files
authored
Merge pull request #3661 from lcaouen/check_settings
show in red incorrect properties in settings.ini
2 parents db2ce5f + d124088 commit 1fc5aaa

3 files changed

Lines changed: 201 additions & 19 deletions

File tree

core/framework/src/main/java/org/phoebus/framework/preferences/PropertyPreferenceWriter.java

Lines changed: 187 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,40 @@
77
*******************************************************************************/
88
package org.phoebus.framework.preferences;
99

10+
import java.io.BufferedReader;
11+
import java.io.ByteArrayInputStream;
12+
import java.io.File;
13+
import java.io.IOException;
14+
import java.io.InputStream;
15+
import java.io.InputStreamReader;
1016
import java.io.OutputStream;
1117
import java.io.OutputStreamWriter;
1218
import java.io.Writer;
19+
import java.nio.file.Files;
20+
import java.nio.file.Path;
21+
import java.nio.file.Paths;
22+
import java.util.*;
23+
import java.util.jar.JarEntry;
24+
import java.util.jar.JarFile;
25+
import java.util.jar.Manifest;
26+
import java.util.logging.Level;
27+
import java.util.logging.Logger;
1328
import java.util.prefs.Preferences;
29+
import java.util.regex.Matcher;
30+
import java.util.regex.Pattern;
31+
import java.util.stream.Collectors;
32+
import java.util.stream.Stream;
1433

1534
/** Write preferences in property file format
1635
* @author Kay Kasemir
1736
*/
1837
@SuppressWarnings("nls")
1938
public class PropertyPreferenceWriter
2039
{
40+
public static final Logger logger = Logger.getLogger(PropertyPreferenceWriter.class.getName());
41+
public static Set<String> excludedKeys = new HashSet<>();
42+
public static Set<String> excludedPackages = new HashSet<>();
43+
2144
/** Save preferences in property file format
2245
*
2346
* <p>Properties have the name "package/setting",
@@ -31,36 +54,185 @@ public class PropertyPreferenceWriter
3154
*/
3255
public static void save(final OutputStream stream) throws Exception
3356
{
57+
Map<String, String> allKeysWithPackages = getAllPropertyKeys();
58+
Preferences prefs = Preferences.userRoot().node("org/phoebus/ui");
59+
60+
String value = prefs.get("excluded_keys_from_settings_check", "");
61+
if (value.isEmpty()) value = allKeysWithPackages.get("org.phoebus.ui/excluded_keys_from_settings_check");
62+
if (!value.isEmpty()) excludedKeys = Arrays.stream(value.split(",")).map(String::trim).collect(Collectors.toSet());
63+
64+
value = prefs.get("excluded_packages_from_settings_check", "");
65+
if (value.isEmpty()) value = allKeysWithPackages.get("org.phoebus.ui/excluded_packages_from_settings_check");
66+
if (!value.isEmpty()) excludedPackages = Arrays.stream(value.split(",")).map(String::trim).collect(Collectors.toSet());
67+
3468
try
3569
(
36-
final OutputStreamWriter out = new OutputStreamWriter(stream);
70+
final OutputStreamWriter out = new OutputStreamWriter(stream)
3771
)
3872
{
39-
out.append("# Preference settings\n");
40-
out.append("# Format:\n");
41-
out.append("# the.package.name/key=value\n");
42-
listSettings(out, Preferences.userRoot());
43-
out.append("# End.\n");
73+
out.append("# Preference settings<br/>\n");
74+
out.append("# Format:<br/>\n");
75+
out.append("# the.package.name/key=value<br/>\n");
76+
out.append("<div style='color: red; font-weight: bold'># key=value in red are incorrect properties</div><br/>\n");
77+
listSettings(allKeysWithPackages, out, Preferences.userRoot());
78+
out.append("<br/>\n");
79+
out.append("# End.<br/>\n");
4480
out.flush();
4581
}
4682
}
4783

48-
private static void listSettings(final Writer out, final Preferences node) throws Exception
84+
private static void listSettings(Map<String, String> allKeysWithPackages, final Writer out, final Preferences node) throws Exception
4985
{
5086
for (String key : node.keys())
51-
formatSetting(out, node, key);
87+
formatSetting(allKeysWithPackages, out, node, key);
5288
for (String child : node.childrenNames())
53-
listSettings(out, node.node(child));
89+
listSettings(allKeysWithPackages, out, node.node(child));
5490
}
5591

56-
private static void formatSetting(final Writer out, final Preferences node, final String key) throws Exception
92+
private static void formatSetting(Map<String, String> allKeysWithPackages, final Writer out, final Preferences node, final String key) throws Exception
5793
{
5894
final String path = node.absolutePath();
59-
out.append(path.substring(1).replace('/', '.'))
60-
.append('/')
61-
.append(key)
95+
String fullKey = path.substring(1).replace('/', '.') + '/' + key;
96+
String keyFound = allKeysWithPackages.get(fullKey);
97+
boolean bNotFound = keyFound == null;
98+
99+
// exclude keys that must not be checked
100+
boolean containsExcludedKeys = excludedKeys.stream().anyMatch(key::contains);
101+
boolean containsExcludedPackages = excludedPackages.stream().anyMatch(fullKey::startsWith);
102+
if (containsExcludedKeys || containsExcludedPackages) bNotFound = false;
103+
104+
if (bNotFound) out.append("<div style='color: red; font-weight: bold'>");
105+
out.append(escapeHtml(fullKey))
62106
.append('=')
63-
.append(node.get(key, ""))
64-
.append('\n');
107+
.append(escapeHtml(node.get(key, "")))
108+
.append("<br/>\n");
109+
if (bNotFound) out.append("</div>");
110+
}
111+
112+
private static Map<String, String> getAllPropertyKeys()
113+
{
114+
Map<String, String> allKeysWithPackages = new HashMap<>();
115+
116+
String classpath = System.getProperty("java.class.path");
117+
String[] jars = classpath.split(File.pathSeparator);
118+
119+
if (jars.length == 1) jars = getAllJarFromManifest(jars[0]);
120+
121+
for (String jarEntry : jars) {
122+
if (jarEntry.endsWith(".jar")) {
123+
File file = new File(jarEntry);
124+
try (JarFile jarFile = new JarFile(file)) {
125+
Enumeration<JarEntry> entries = jarFile.entries();
126+
127+
while (entries.hasMoreElements()) {
128+
JarEntry entry = entries.nextElement();
129+
String entryName = entry.getName();
130+
131+
if (entryName.endsWith("preferences.properties")) {
132+
parsePropertiesWithPackage(
133+
jarFile.getInputStream(entry),
134+
entryName,
135+
allKeysWithPackages
136+
);
137+
}
138+
}
139+
} catch (IOException ex) {
140+
logger.log(Level.WARNING, "Error opening JAR : " + jarEntry, ex);
141+
}
142+
}
143+
else if (jarEntry.endsWith("classes")) {
144+
Path startPath = Paths.get(jarEntry);
145+
String filePattern = "preferences.properties";
146+
147+
try (Stream<Path> paths = Files.walk(startPath)) {
148+
paths.filter(path -> path.toString().endsWith(filePattern))
149+
.forEach(path -> {
150+
try (InputStream inputStream = Files.newInputStream(path)) {
151+
parsePropertiesWithPackage(inputStream, path.getFileName().toString(), allKeysWithPackages);
152+
} catch (IOException ex) {
153+
logger.log(Level.WARNING, "Error opening properties file : " + path, ex);
154+
}
155+
});
156+
} catch (IOException ex) {
157+
logger.log(Level.WARNING, "Error listing files in : " + startPath, ex);
158+
}
159+
}
160+
}
161+
162+
return allKeysWithPackages;
163+
}
164+
165+
private static String[] getAllJarFromManifest(String jarPath) {
166+
String[] jars = new String[0];
167+
File jarFile = new File(jarPath);
168+
169+
try (JarFile jar = new JarFile(jarFile)) {
170+
Manifest manifest = jar.getManifest();
171+
172+
if (manifest != null) {
173+
String classPath = manifest.getMainAttributes().getValue("Class-Path");
174+
175+
if (classPath != null && !classPath.isEmpty()) {
176+
jars = classPath.split(" ");
177+
178+
for (int iJar = 0; iJar < jars.length; iJar++) {
179+
Path fullPath = Paths.get(jarFile.getParent()).resolve(jars[iJar]);
180+
jars[iJar] = fullPath.toString();
181+
}
182+
} else {
183+
logger.log(Level.WARNING, "No Class-Path found in MANIFEST.MF " + jarPath);
184+
}
185+
} else {
186+
logger.log(Level.WARNING, "MANIFEST.MF not found in the JAR " + jarPath);
187+
}
188+
} catch (IOException ex) {
189+
logger.log(Level.WARNING, "Error when reading the jar : " + jarPath, ex);
190+
}
191+
192+
return jars;
193+
}
194+
195+
private static void parsePropertiesWithPackage(InputStream inputStream, String fileName, Map<String, String> allKeysWithPackages) {
196+
Properties props = new Properties();
197+
String packageName = null;
198+
199+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
200+
String line;
201+
StringBuilder content = new StringBuilder();
202+
203+
while ((line = reader.readLine()) != null) {
204+
line = line.trim();
205+
if (line.startsWith("#") && line.contains("Package")) {
206+
// Find package name
207+
Pattern pattern = Pattern.compile("#\\s*Package\\s+(\\S+)");
208+
Matcher matcher = pattern.matcher(line);
209+
if (matcher.find()) {
210+
packageName = matcher.group(1);
211+
}
212+
} else if (!line.startsWith("#")) {
213+
content.append(line).append("\n");
214+
}
215+
}
216+
217+
if (!content.isEmpty()) {
218+
props.load(new ByteArrayInputStream(content.toString().getBytes()));
219+
}
220+
221+
// properties found
222+
for (String key : props.stringPropertyNames()) {
223+
String prefixedKey = (packageName != null) ? packageName + "/" + key : key;
224+
allKeysWithPackages.put(prefixedKey, props.getProperty(key));
225+
}
226+
} catch (IOException ex) {
227+
logger.log(Level.WARNING, "Error when reading file " + fileName, ex);
228+
}
229+
}
230+
231+
private static String escapeHtml(String input) {
232+
return input.replace("&", "&amp;")
233+
.replace("<", "&lt;")
234+
.replace(">", "&gt;")
235+
.replace("\"", "&quot;")
236+
.replace("'", "&#39;");
65237
}
66238
}

core/ui/src/main/java/org/phoebus/ui/help/OpenAbout.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import javafx.scene.image.Image;
4343
import javafx.scene.layout.Priority;
4444
import javafx.scene.layout.VBox;
45+
import javafx.scene.web.WebView;
4546

4647
/** Menu entry to open 'about'
4748
* @author Kay Kasemir
@@ -204,12 +205,17 @@ private Node createDetailSection()
204205
logger.log(Level.WARNING, "Cannot list preferences", ex);
205206
}
206207

207-
area = new TextArea(prefs_buf.toString());
208-
area.setEditable(false);
208+
WebView webView = new WebView();
209+
String content = "<html><head><style>" +
210+
"body {font-family: monospace;}" +
211+
"</style></head><body>";
212+
content += prefs_buf.toString();
213+
content += "</body></html>";
214+
webView.getEngine().loadContent(content);
209215

210-
VBox.setVgrow(area, Priority.ALWAYS);
216+
VBox.setVgrow(webView, Priority.ALWAYS);
211217

212-
final Tab prefs = new Tab(Messages.HelpAboutPrefs, area);
218+
final Tab prefs = new Tab(Messages.HelpAboutPrefs, webView);
213219

214220
final TabPane tabs = new TabPane(apps, envs, props, prefs);
215221
return tabs;

core/ui/src/main/resources/phoebus_ui_preferences.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,7 @@ default_window_title=CS-Studio
290290
# For a system file use syntax; 'file:</path/to/custom.css>'
291291
# For a file served over http use syntax: 'http://<address:port/custom.css>'
292292
custom_css_styling=
293+
294+
# Keywords that can be excluded from the settings check in the About Dialog
295+
excluded_keys_from_settings_check=external_app,password,username
296+
excluded_packages_from_settings_check=eu.ess,fr.cea

0 commit comments

Comments
 (0)