From 56221266b8fa99720da7ff2ad31ee4f8e7489047 Mon Sep 17 00:00:00 2001 From: Nicola Isotta Date: Thu, 22 Jan 2026 19:00:52 +0100 Subject: [PATCH 1/4] improve resourcebundles handling --- .../web.el/nbproject/project.properties | 2 +- .../modules/web/el/ELOccurrencesFinder.java | 18 +- .../modules/web/el/ResourceBundles.java | 287 ++++++++++++++---- .../completion/ELCodeCompletionHandler.java | 15 +- .../web/el/completion/ELCompletionUtil.java | 31 ++ .../web/el/hints/ResourceBundleKeys.java | 2 +- .../el/navigation/ELDeclarationFinder.java | 61 ++-- .../el/navigation/ELHyperlinkProvider.java | 12 +- .../modules/web/el/spi/ResourceBundle.java | 13 +- .../editor/JSFResourceBundlesProvider.java | 77 +++-- 10 files changed, 383 insertions(+), 135 deletions(-) create mode 100644 enterprise/web.el/src/org/netbeans/modules/web/el/completion/ELCompletionUtil.java diff --git a/enterprise/web.el/nbproject/project.properties b/enterprise/web.el/nbproject/project.properties index f753aa35afd5..c81e65023d7c 100644 --- a/enterprise/web.el/nbproject/project.properties +++ b/enterprise/web.el/nbproject/project.properties @@ -14,7 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -javac.source=1.8 +javac.release=17 javac.compilerargs=-Xlint -Xlint:-serial test-unit-sys-prop.web.project.jars=\ diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/ELOccurrencesFinder.java b/enterprise/web.el/src/org/netbeans/modules/web/el/ELOccurrencesFinder.java index 1134afae8f0c..ea0c1fc892ea 100644 --- a/enterprise/web.el/src/org/netbeans/modules/web/el/ELOccurrencesFinder.java +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/ELOccurrencesFinder.java @@ -133,19 +133,19 @@ private void computeOccurrences(final ELParserResult parserResult) { try { jsource.runUserActionTask((CompilationController info) -> { info.toPhase(JavaSource.Phase.RESOLVED); - occurrences.putAll(findMatchingTypes(CompilationContext.create(file, info), target, matching)); + CompilationContext ccontext = CompilationContext.create(file, info); + occurrences.putAll(findMatchingTypes(ccontext, target, matching)); + if (this.occurrences.isEmpty()) { + // perhaps the caret is on a resource bundle key node + occurrences.putAll(findMatchingResourceBundleKeys(ccontext, target, parserResult)); + } }, true); } catch (IOException ex) { Exceptions.printStackTrace(ex); } - - if (this.occurrences.isEmpty()) { - // perhaps the caret is on a resource bundle key node - occurrences.putAll(findMatchingResourceBundleKeys(target, parserResult)); - } } - private Map findMatchingResourceBundleKeys(Pair target, ELParserResult parserResult) { + private Map findMatchingResourceBundleKeys(CompilationContext info, Pair target, ELParserResult parserResult) { ResourceBundles resourceBundles = ResourceBundles.get(parserResult.getFileObject()); if (!resourceBundles.canHaveBundles()) { return Collections.emptyMap(); @@ -154,7 +154,7 @@ private Map findMatchingResourceBundleKeys(Pair // the logic here is a bit strange, maybe should add new methods to ResourceBundles // for a more straightforward computation. // first, check whether the current EL elements has keys - keys.addAll(resourceBundles.collectKeys(target.first().getNode())); + keys.addAll(resourceBundles.collectKeys(target.first().getNode(), info.context())); if (keys.isEmpty()) { return Collections.emptyMap(); } @@ -176,7 +176,7 @@ private Map findMatchingResourceBundleKeys(Pair if (!each.isValid()) { continue; } - for (Pair candidate : resourceBundles.collectKeys(each.getNode())) { + for (Pair candidate : resourceBundles.collectKeys(each.getNode(), info.context())) { if (candidate.second().equals(target.second())) { OffsetRange range = each.getOriginalOffset(candidate.second()); result.put(range, ColoringAttributes.MARK_OCCURRENCES); diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/ResourceBundles.java b/enterprise/web.el/src/org/netbeans/modules/web/el/ResourceBundles.java index 5bf24f336df5..c24bd83873d4 100644 --- a/enterprise/web.el/src/org/netbeans/modules/web/el/ResourceBundles.java +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/ResourceBundles.java @@ -24,8 +24,8 @@ import com.sun.el.parser.AstString; import com.sun.el.parser.Node; import java.io.IOException; -import java.net.URL; import java.util.*; +import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.text.BadLocationException; import javax.swing.text.StyledDocument; @@ -72,7 +72,7 @@ public final class ResourceBundles { private final WebModule webModule; private final Project project; - + /* bundle base name to ResourceBundleInfo map */ private Map bundlesMap; private long currentBundlesHashCode; @@ -82,12 +82,12 @@ public final class ResourceBundles { @Override public void fileChanged(FileEvent fe) { super.fileChanged(fe); - LOGGER.finer(String.format("File %s has changed.", fe.getFile())); //NOI18N + LOGGER.log(Level.FINER, "File {0} has changed.", fe.getFile()); //NOI18N resetResourceBundleMap(); } }; - + private ResourceBundles(WebModule webModule, Project project) { this.webModule = webModule; this.project = project; @@ -143,7 +143,19 @@ public boolean isResourceBundleIdentifier(String identifier, ResolverContext con * {@code key}; {@code false} otherwise. */ public boolean isValidKey(String bundle, String key) { - ResourceBundleInfo rbInfo = getBundleForIdentifier(bundle); + return isValidKey(new ResolverContext(), bundle, key); + } + + /** + * Checks whether the given {@code key} is defined in the given {@code bundle}. + * @param context non-null {@link ResolverContext} instance + * @param bundle the base name of the bundle. + * @param key the key to check. + * @return {@code true} if the given {@code bundle} exists and contains the given + * {@code key}; {@code false} otherwise. + */ + public boolean isValidKey(ResolverContext context, String bundle, String key) { + ResourceBundleInfo rbInfo = getBundleForIdentifier(context, bundle); if (rbInfo == null) { // no matching bundle file return true; @@ -158,12 +170,13 @@ public boolean isValidKey(String bundle, String key) { /** * Gets bundle info for given identifier. + * @param context non-null {@link ResolverContext} instance * @param ident identifier to examine * @return resource bundle info if any found, {@code null} otherwise */ - private ResourceBundleInfo getBundleForIdentifier(String ident) { + private ResourceBundleInfo getBundleForIdentifier(ResolverContext context, String ident) { // XXX - do it more efficiently - for (ResourceBundleInfo rbi : getBundlesMap().values()) { + for (ResourceBundleInfo rbi : getBundlesMap(context).values()) { if (ident.equals(rbi.getVarName())) { return rbi; } @@ -177,16 +190,21 @@ private ResourceBundleInfo getBundleForIdentifier(String ident) { * @return locations corresponding to given bundle name, never {@code null} */ public List getLocationsForBundleIdent(String ident) { - ResourceBundleInfo rbi = getBundleForIdentifier(ident); + return getLocationsForBundleIdent(new ResolverContext(), ident); + } + + /** + * Gets all locations for given bundle identifier. + * @param context non-null {@link ResolverContext} instance + * @param ident identifier of the bundle + * @return locations corresponding to given bundle name, never {@code null} + */ + public List getLocationsForBundleIdent(ResolverContext context, String ident) { + ResourceBundleInfo rbi = getBundleForIdentifier(context, ident); if (rbi == null) { return Collections.emptyList(); } - - List locations = new ArrayList<>(rbi.getFiles().size()); - for (FileObject fileObject : rbi.getFiles()) { - locations.add(new Location(0, fileObject)); - } - return locations; + return rbi.getFiles().stream().map(Location::new).toList(); } /** @@ -196,8 +214,19 @@ public List getLocationsForBundleIdent(String ident) { * @return locations (including the offset) of the searched key, never {@code null} */ public List getLocationsForBundleKey(String ident, String key) { + return getLocationsForBundleKey(new ResolverContext(), ident, key); + } + + /** + * Gets all locations for given bundle identifier and key. + * @param context non-null {@link ResolverContext} instance + * @param ident identifier of the bundle + * @param key key to search + * @return locations (including the offset) of the searched key, never {@code null} + */ + public List getLocationsForBundleKey(ResolverContext context, String ident, String key) { List locations = new ArrayList<>(); - for (Location location : getLocationsForBundleIdent(ident)) { + for (Location location : getLocationsForBundleIdent(context, ident)) { try { DataObject dobj = DataObject.find(location.getFile()); EditorCookie ec = dobj.getLookup().lookup(EditorCookie.class); @@ -210,11 +239,17 @@ public List getLocationsForBundleKey(String ident, String key) { if (lc != null) { Line.Set ls = lc.getLineSet(); for (Line line : ls.getLines()) { - if (line.getText().contains(key + "=") || line.getText().contains(key + " =")) { //NOI18N + String text = line.getText(); + if (text == null) { + continue; + } + text = text.trim(); + if (text.startsWith(key + "=") || text.startsWith(key + " =")) { //NOI18N try { StyledDocument document = ec.getDocument(); int offset = document.getText(0, document.getLength()).indexOf(line.getText()); locations.add(new Location(offset, location.getFile())); + break; } catch (BadLocationException ex) { Exceptions.printStackTrace(ex); } @@ -234,6 +269,7 @@ public List> collectKeys(final Node root) { /** * Collects references to resource bundle keys in the given {@code root}. + * @param context non-null {@link ResolverContext} instance * @return List of identifier/string pairs. Identifier = resource bundle base name - string = res bundle key. */ public List> collectKeys(final Node root, ResolverContext context) { @@ -261,6 +297,10 @@ public List> collectKeys(final Node root, ResolverCont } public String findResourceBundleIdentifier(AstPath astPath) { + return findResourceBundleIdentifier(new ResolverContext(), astPath); + } + + public String findResourceBundleIdentifier(ResolverContext context, AstPath astPath) { List path = astPath.leafToRoot(); for (int i = 0; i < path.size(); i++) { Node node = path.get(i); @@ -273,7 +313,7 @@ public String findResourceBundleIdentifier(AstPath astPath) { Node identifier = path.get(i + 2); if (brackets instanceof AstBracketSuffix && identifier instanceof AstIdentifier - && isResourceBundleIdentifier(identifier.getImage(), new ResolverContext())) { + && isResourceBundleIdentifier(identifier.getImage(), context)) { return identifier.getImage(); } } @@ -281,6 +321,7 @@ && isResourceBundleIdentifier(identifier.getImage(), new ResolverContext())) { } return null; } + /** * Gets the value of the given {@code key} in the given {@code bundle}. * @param bundle the base name of the bundle. @@ -288,7 +329,48 @@ && isResourceBundleIdentifier(identifier.getImage(), new ResolverContext())) { * @return the value or {@code null}. */ public String getValue(String bundle, String key) { - ResourceBundleInfo rbInfo = getBundlesMap().get(bundle); + return getValue(new ResolverContext(), bundle, key); + } + + /** + * Gets the value of the given {@code key} in the given {@code bundle}. + * @param context non-null {@link ResolverContext} instance + * @param bundle the base name of the bundle. + * @param key key in the given bundle. + * @return the value or {@code null}. + */ + public String getValue(ResolverContext context, String bundle, String key) { + ResourceBundleInfo rbInfo = getBundlesMap(context).get(bundle); + if (rbInfo == null || !rbInfo.getResourceBundle().containsKey(key)) { + // no matching bundle file + return null; + } + try { + return rbInfo.getResourceBundle().getString(key); + } catch (MissingResourceException e) { + return null; + } + } + + /** + * Gets the value of the given {@code key} in the given {@code bundle}. + * @param varName the var name of the bundle. + * @param key key in the given bundle. + * @return the value or {@code null}. + */ + public String getValueWithVarName(String varName, String key) { + return getValueWithVarName(new ResolverContext(), varName, key); + } + + /** + * Gets the value of the given {@code key} in the given {@code bundle}. + * @param context non-null {@link ResolverContext} instance + * @param varName the var name of the bundle. + * @param key key in the given bundle. + * @return the value or {@code null}. + */ + public String getValueWithVarName(ResolverContext context, String varName, String key) { + ResourceBundleInfo rbInfo = getBundleForIdentifier(context, varName); if (rbInfo == null || !rbInfo.getResourceBundle().containsKey(key)) { // no matching bundle file return null; @@ -306,8 +388,18 @@ public String getValue(String bundle, String key) { * @return */ public Map getEntries(String bundleVar) { - ResourceBundle bundle = findResourceBundleForVar(bundleVar); - ResourceBundleInfo rbInfo = getBundlesMap().get(bundle.getBaseName()); + return getEntries(new ResolverContext(), bundleVar); + } + + /** + * Gets the entries in the bundle identified by {@code bundleName}. + * @param context non-null {@link ResolverContext} instance + * @param bundleVar + * @return + */ + public Map getEntries(ResolverContext context, String bundleVar) { + ResourceBundle bundle = findResourceBundleForVar(context, bundleVar); + ResourceBundleInfo rbInfo = getBundlesMap(context).get(bundle.getBaseName()); if (rbInfo == null) { return Collections.emptyMap(); } @@ -318,11 +410,10 @@ public Map getEntries(String bundleVar) { } return result; } - - - private ResourceBundle findResourceBundleForVar(String variableName) { + + private ResourceBundle findResourceBundleForVar(ResolverContext context, String variableName) { List foundBundles = webModule != null ? - ELPlugin.Query.getResourceBundles(webModule.getDocumentBase(), new ResolverContext()) + ELPlugin.Query.getResourceBundles(webModule.getDocumentBase(), context) : Collections.emptyList(); //make the bundle var to bundle @@ -333,9 +424,11 @@ private ResourceBundle findResourceBundleForVar(String variableName) { } return null; } + /** * Finds list of all ResourceBundles, which are registered in all * JSF configuration files in a web module. + * @param context non-null {@link ResolverContext} instance */ public List getBundles(ResolverContext context) { FileObject docBase = webModule != null ? webModule.getDocumentBase() : null; @@ -345,17 +438,17 @@ public List getBundles(ResolverContext context) { /* * returns a map of bundle fully qualified name to java.util.ResourceBundle */ - private synchronized Map getBundlesMap() { - long bundlesHash = getBundlesHashCode(); + private synchronized Map getBundlesMap(ResolverContext context) { + long bundlesHash = getBundlesHashCode(context); if (bundlesMap == null) { currentBundlesHashCode = bundlesHash; - bundlesMap = createResourceBundleMapAndFileChangeListeners(); + bundlesMap = createResourceBundleMapAndFileChangeListeners(context); LOGGER.fine("New resource bundle map created."); //NOI18N } else { if(bundlesHash != currentBundlesHashCode) { //refresh the resource bundle map resetResourceBundleMap(); - bundlesMap = createResourceBundleMapAndFileChangeListeners(); + bundlesMap = createResourceBundleMapAndFileChangeListeners(context); currentBundlesHashCode = bundlesHash; LOGGER.fine("Resource bundle map recreated based on configuration changes."); //NOI18N @@ -364,7 +457,7 @@ private synchronized Map getBundlesMap() { return bundlesMap; } - + private synchronized void resetResourceBundleMap() { if(bundlesMap == null) { return ; @@ -372,25 +465,25 @@ private synchronized void resetResourceBundleMap() { for(ResourceBundleInfo info : bundlesMap.values()) { for (FileObject fileObject : info.getFiles()) { fileObject.removeFileChangeListener(FILE_CHANGE_LISTENER); - LOGGER.finer(String.format("Removed FileChangeListener from file %s", fileObject)); //NOI18N + LOGGER.log(Level.FINER, "Removed FileChangeListener from file {0}", fileObject); //NOI18N } } bundlesMap = null; LOGGER.fine("Resource bundle map released."); //NOI18N } - - private long getBundlesHashCode() { + + private long getBundlesHashCode(ResolverContext context) { //compute hashcode so we can compare if there are changes since the last time and possibly //reset the bundle map cache long hash = 3; - for(ResourceBundle rb : getBundles(new ResolverContext())) { + for(ResourceBundle rb : getBundles(context)) { hash = 11 * hash + rb.getBaseName().hashCode(); hash = 11 * hash + (rb.getVar() != null ? rb.getVar().hashCode() : 0); } return hash; } - - private Map createResourceBundleMapAndFileChangeListeners() { + + private Map createResourceBundleMapAndFileChangeListeners(ResolverContext context) { Map result = new HashMap<>(); ClassPathProvider provider = project.getLookup().lookup(ClassPathProvider.class); if (provider == null) { @@ -403,8 +496,26 @@ private Map createResourceBundleMapAndFileChangeList } SourceGroup[] sourceGroups = sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA); - for (ResourceBundle bundle : getBundles(new ResolverContext())) { + for (ResourceBundle bundle : getBundles(context)) { String bundleFile = bundle.getBaseName(); + + for (FileObject fileObject : bundle.getFiles()) { + if (fileObject.canWrite()) { + fileObject.addFileChangeListener( + WeakListeners.create(FileChangeListener.class, FILE_CHANGE_LISTENER, fileObject)); + LOGGER.log(Level.FINER, "Added FileChangeListener to file {0}", fileObject); + } + } + + java.util.ResourceBundle found = null; + if (!bundle.getFiles().isEmpty()) { + found = loadBundleChain(bundle.getFiles()); + } + if (found != null) { + result.put(bundleFile, new ResourceBundleInfo(bundle.getFiles(), found, bundle.getVar())); + continue; + } + for (SourceGroup sourceGroup : sourceGroups) { FileObject rootFolder = sourceGroup.getRootFolder(); @@ -415,40 +526,94 @@ private Map createResourceBundleMapAndFileChangeList } ClassLoader classLoader = classPath.getClassLoader(false); try { - // TODO - rewrite listening on all (localized) files - String resourceFileName = new StringBuilder() - .append(bundleFile.replace(".", "/")) - .append(".properties") - .toString(); //NOI18N - - URL url = classLoader.getResource(resourceFileName); - if(url != null) { - LOGGER.finer(String.format("Found %s URL for resource bundle %s", url, resourceFileName )); - FileObject fileObject = URLMapper.findFileObject(url); - if(fileObject != null) { - if (fileObject.canWrite()) { - fileObject.addFileChangeListener( - WeakListeners.create(FileChangeListener.class, FILE_CHANGE_LISTENER, fileObject)); - LOGGER.finer(String.format("Added FileChangeListener to file %s", fileObject )); - } - } else { - LOGGER.fine(String.format("Cannot map %s URL to FileObject!", url)); - } - } - - java.util.ResourceBundle found = java.util.ResourceBundle.getBundle(bundleFile, Locale.getDefault(), classLoader); + found = java.util.ResourceBundle.getBundle(bundleFile, Locale.getDefault(), classLoader); result.put(bundleFile, new ResourceBundleInfo(bundle.getFiles(), found, bundle.getVar())); break; // found the bundle in source cp, skip searching compile cp } catch (MissingResourceException exception) { continue; } } - } } return result; } + private java.util.ResourceBundle loadBundleChain(List files) { + Locale defaultLocale = Locale.getDefault(); + String lang = defaultLocale.getLanguage(); + String country = defaultLocale.getCountry(); + + FileObject baseFile = null; + FileObject langFile = null; + FileObject countryFile = null; + FileObject fallbackFile = null; + + for (FileObject fo : files) { + String name = fo.getName(); + + if (fallbackFile == null) { + fallbackFile = fo; + } + + if (!name.contains("_")) { + baseFile = fo; + } else if (name.endsWith("_" + lang)) { + langFile = fo; + } else if (name.endsWith("_" + lang + "_" + country)) { + countryFile = fo; + } + } + java.util.ResourceBundle chainHead = null; + java.util.ResourceBundle currentParent = null; + if (baseFile != null) { + currentParent = createBundleSafe(baseFile); + chainHead = currentParent; + } + if (langFile != null) { + LinkableResourceBundle langBundle = createBundleSafe(langFile); + if (langBundle != null) { + if (currentParent != null) { + langBundle.setChainParent(currentParent); + } + currentParent = langBundle; + chainHead = langBundle; + } + } + if (countryFile != null) { + LinkableResourceBundle countryBundle = createBundleSafe(countryFile); + if (countryBundle != null) { + if (currentParent != null) { + countryBundle.setChainParent(currentParent); + } + chainHead = countryBundle; + } + } + if (chainHead == null && fallbackFile != null) { + return createBundleSafe(fallbackFile); + } + return chainHead; + } + + private LinkableResourceBundle createBundleSafe(FileObject fo) { + try (java.io.InputStream in = fo.getInputStream()) { + return new LinkableResourceBundle(in); + } catch (IOException ex) { + LOGGER.log(Level.FINE, "Error loading properties file: {0}", fo.getPath()); + return null; + } + } + + private static class LinkableResourceBundle extends PropertyResourceBundle { + + public LinkableResourceBundle(java.io.InputStream stream) throws IOException { + super(stream); + } + + public void setChainParent(java.util.ResourceBundle parent) { + setParent(parent); + } + } + private static final class ResourceBundleInfo { private final List files; private final java.util.ResourceBundle resourceBundle; @@ -478,6 +643,10 @@ public static class Location { private final int offset; private final FileObject file; + public Location(FileObject file) { + this(0, file); + } + public Location(int offset, FileObject file) { this.offset = offset; this.file = file; diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/completion/ELCodeCompletionHandler.java b/enterprise/web.el/src/org/netbeans/modules/web/el/completion/ELCodeCompletionHandler.java index 2524ec80fb3d..cc9b3f3b50bb 100644 --- a/enterprise/web.el/src/org/netbeans/modules/web/el/completion/ELCodeCompletionHandler.java +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/completion/ELCodeCompletionHandler.java @@ -76,6 +76,7 @@ import org.netbeans.modules.web.el.spi.ELPlugin; import org.netbeans.modules.web.el.spi.ELVariableResolver.VariableInfo; import org.netbeans.modules.web.el.spi.Function; +import org.netbeans.modules.web.el.spi.ResolverContext; import org.netbeans.modules.web.el.spi.ResourceBundle; import org.openide.filesystems.FileObject; import org.openide.util.Exceptions; @@ -176,7 +177,7 @@ public void run(CompilationController info) throws Exception { // seems to be something like "sessionScope.^", so complete beans from the scope proposeBeansFromScope(ccontext, context, prefixMatcher, element, node, proposals); } else if (ELTypeUtilities.isResourceBundleVar(ccontext, node)) { - proposeBundleKeysInDotNotation(context, prefixMatcher, element, node, proposals); + proposeBundleKeysInDotNotation(ccontext, context, prefixMatcher, element, node, proposals); } else if (resolved == null) { if (target instanceof AstDotSuffix == false && node instanceof AstFunction == false) { proposeFunctions(ccontext, context, prefixMatcher, element, proposals); @@ -603,12 +604,13 @@ private void proposeBundleKeysInArrayNotation(CodeCompletionContext context, if (!resourceBundles.canHaveBundles()) { return; } + ResolverContext resolverContext = new ResolverContext(); FileObject bundleFile = null; - List bundleLocations = resourceBundles.getLocationsForBundleIdent(bundleKey); + List bundleLocations = resourceBundles.getLocationsForBundleIdent(resolverContext, bundleKey); if (!bundleLocations.isEmpty()) { bundleFile = bundleLocations.get(0).getFile(); } - for (Map.Entry entry : resourceBundles.getEntries(bundleKey).entrySet()) { + for (Map.Entry entry : resourceBundles.getEntries(resolverContext, bundleKey).entrySet()) { if (!prefix.matches(entry.getKey())) { continue; } @@ -620,7 +622,8 @@ private void proposeBundleKeysInArrayNotation(CodeCompletionContext context, } // "msg.key" notation - private void proposeBundleKeysInDotNotation(CodeCompletionContext context, + private void proposeBundleKeysInDotNotation(CompilationContext ccontext, + CodeCompletionContext context, PrefixMatcher prefix, ELElement elElement, Node baseObjectNode, @@ -632,11 +635,11 @@ private void proposeBundleKeysInDotNotation(CodeCompletionContext context, return; } FileObject bundleFile = null; - List bundleLocations = resourceBundles.getLocationsForBundleIdent(bundleKey); + List bundleLocations = resourceBundles.getLocationsForBundleIdent(ccontext.context(), bundleKey); if (!bundleLocations.isEmpty()) { bundleFile = bundleLocations.get(0).getFile(); } - for (Map.Entry entry : resourceBundles.getEntries(bundleKey).entrySet()) { + for (Map.Entry entry : resourceBundles.getEntries(ccontext.context(), bundleKey).entrySet()) { if (!prefix.matches(entry.getKey())) { continue; } diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/completion/ELCompletionUtil.java b/enterprise/web.el/src/org/netbeans/modules/web/el/completion/ELCompletionUtil.java new file mode 100644 index 000000000000..144a2e394478 --- /dev/null +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/completion/ELCompletionUtil.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.web.el.completion; + +import org.netbeans.modules.csl.api.ElementHandle; +import org.netbeans.modules.web.el.ELElement; +import org.openide.filesystems.FileObject; + +public class ELCompletionUtil { + + public static ElementHandle getResourceBundleElementHandle(String key, String value, ELElement element, FileObject bundleFile) { + return new ELResourceBundleKeyCompletionItem(key, value, element, bundleFile).getElement(); + } + +} diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/hints/ResourceBundleKeys.java b/enterprise/web.el/src/org/netbeans/modules/web/el/hints/ResourceBundleKeys.java index 76a1f2c7e750..1a3eb8f48bb1 100644 --- a/enterprise/web.el/src/org/netbeans/modules/web/el/hints/ResourceBundleKeys.java +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/hints/ResourceBundleKeys.java @@ -73,7 +73,7 @@ protected void run(CompilationContext info, RuleContext context, List resu } for (Pair pair : resourceBundles.collectKeys(each.getNode(), info.context())) { String clearedKey = pair.second().getImage().replace("'", "").replace("\"", ""); - if (!resourceBundles.isValidKey(pair.first().getImage(), clearedKey)) { + if (!resourceBundles.isValidKey(info.context(), pair.first().getImage(), clearedKey)) { Hint hint = new Hint(this, NbBundle.getMessage(ResourceBundleKeys.class, "ResourceBundleKeys_Unknown", clearedKey), elResult.getFileObject(), diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELDeclarationFinder.java b/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELDeclarationFinder.java index ce3dd96158ab..177d7548595a 100644 --- a/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELDeclarationFinder.java +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELDeclarationFinder.java @@ -41,7 +41,6 @@ import org.netbeans.api.java.source.Task; import org.netbeans.api.project.FileOwnerQuery; import org.netbeans.api.project.Project; -import org.netbeans.modules.csl.api.DataLoadersBridge; import org.netbeans.modules.csl.api.DeclarationFinder; import org.netbeans.modules.csl.api.ElementKind; import org.netbeans.modules.csl.api.HtmlFormatter; @@ -54,6 +53,7 @@ import org.netbeans.modules.web.el.ELElement; import org.netbeans.modules.web.el.ELTypeUtilities; import org.netbeans.modules.web.el.ResourceBundles; +import org.netbeans.modules.web.el.completion.ELCompletionUtil; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.Exceptions; @@ -95,12 +95,13 @@ public void run(CompilationController cc) throws Exception { // resolve resource bundles ResourceBundles resourceBundles = ResourceBundles.get(file); if (resourceBundles.canHaveBundles()) { - List bundleLocations = getBundleLocations(resourceBundles, nodeElem); + List bundleLocations = getBundleLocations(context, resourceBundles, nodeElem); if (!bundleLocations.isEmpty()) { - refs.fo = bundleLocations.get(0).getFile(); - refs.offset = bundleLocations.get(0).getOffset(); - for (ResourceBundles.Location location : bundleLocations) { - alternatives.add(new ResourceBundleAlternative(location)); + refs.fo = bundleLocations.get(0).fo; + refs.offset = bundleLocations.get(0).offset; + refs.elementHandle = bundleLocations.get(0).elementHandle; + for (RefsHolder location : bundleLocations) { + alternatives.add(new ResourceBundleAlternative(location.fo, location.offset)); } } } @@ -128,7 +129,7 @@ public void run(CompilationController controller) throws Exception { } if (refs.fo != null && refs.offset != -1) { - DeclarationLocation declarationLocation = new DeclarationLocation(refs.fo, refs.offset); + DeclarationLocation declarationLocation = new DeclarationLocation(refs.fo, refs.offset, refs.elementHandle); for (AlternativeLocation alternativeLocation : alternatives) { declarationLocation.addAlternative(alternativeLocation); } @@ -152,9 +153,10 @@ public void run() { return ret.get(); } - private static List getBundleLocations(ResourceBundles resourceBundles, Pair nodeElem) { + private static List getBundleLocations(CompilationContext ccontext, ResourceBundles resourceBundles, Pair nodeElem) { if (nodeElem.first() instanceof AstIdentifier) { - return resourceBundles.getLocationsForBundleIdent(nodeElem.first().getImage()); + List locations = resourceBundles.getLocationsForBundleIdent(ccontext.context(), nodeElem.first().getImage()); + return locations.stream().map(location -> new RefsHolder(location.getFile(), location.getOffset())).toList(); } else { AstPath astPath = new AstPath(nodeElem.second().getNode()); for (Node node : astPath.rootToLeaf()) { @@ -165,11 +167,20 @@ private static List getBundleLocations(ResourceBundles // bundle['key'] image = image.substring(1, image.length() - 1); } - return resourceBundles.getLocationsForBundleKey(node.getImage(), image); + final String key = image; + List locations = resourceBundles.getLocationsForBundleKey(ccontext.context(), node.getImage(), key); + final String value; + if (!locations.isEmpty()) { + value = resourceBundles.getValueWithVarName(ccontext.context(), node.getImage(), key); + } else { + value = null; + } + return locations.stream().map(location -> new RefsHolder(location.getFile(), location.getOffset(), + ELCompletionUtil.getResourceBundleElementHandle(key, value, nodeElem.second(), location.getFile()))).toList(); } } } - return Collections.emptyList(); + return Collections.emptyList(); } private static class ResourceBundleAlternative implements AlternativeLocation { @@ -177,9 +188,9 @@ private static class ResourceBundleAlternative implements AlternativeLocation { private final FileObject file; private final int offset; - public ResourceBundleAlternative(ResourceBundles.Location location) { - this.offset = location.getOffset(); - this.file = location.getFile(); + public ResourceBundleAlternative(FileObject file, int offset) { + this.offset = offset; + this.file = file; } @Override @@ -274,9 +285,25 @@ public boolean equals(Object obj) { private static class RefsHolder { - private ElementHandle handle; - private FileObject fo; - private int offset = -1; + ElementHandle handle; + FileObject fo; + int offset = -1; + org.netbeans.modules.csl.api.ElementHandle elementHandle; + + RefsHolder() { + } + + RefsHolder(FileObject fo, int offset) { + this.fo = fo; + this.offset = offset; + } + + public RefsHolder(FileObject fo, int offset, org.netbeans.modules.csl.api.ElementHandle elementHandle) { + this.fo = fo; + this.offset = offset; + this.elementHandle = elementHandle; + } + } private static class ResourceBundleElementHandle implements org.netbeans.modules.csl.api.ElementHandle { diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELHyperlinkProvider.java b/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELHyperlinkProvider.java index ca46365d37d1..6cd1611a572e 100644 --- a/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELHyperlinkProvider.java +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELHyperlinkProvider.java @@ -84,7 +84,7 @@ public final class ELHyperlinkProvider implements HyperlinkProviderExt { @Override - public boolean isHyperlinkPoint(final Document doc, final int offset, HyperlinkType type) { + public boolean isHyperlinkPoint(final Document doc, final int offset, HyperlinkType type) { final AtomicBoolean ret = new AtomicBoolean(false); doc.render(new Runnable() { @@ -130,12 +130,14 @@ public Set getSupportedHyperlinkTypes() { public String getTooltipText(Document doc, int offset, HyperlinkType type) { Pair nodeAndElement = resolveNodeAndElement(doc, offset, new AtomicBoolean()); if (nodeAndElement != null) { - if (nodeAndElement.first() instanceof AstString) { + if (nodeAndElement.first() instanceof AstString || nodeAndElement.first() instanceof AstDotSuffix) { // could be a resource bundle key - return getTooltipTextForBundleKey(nodeAndElement); - } else { - return getTooltipTextForElement(nodeAndElement); + String tooltip = getTooltipTextForBundleKey(nodeAndElement); + if (tooltip != null) { + return tooltip; + } } + return getTooltipTextForElement(nodeAndElement); } return null; } diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/spi/ResourceBundle.java b/enterprise/web.el/src/org/netbeans/modules/web/el/spi/ResourceBundle.java index 94cef123a217..a72317b69d23 100644 --- a/enterprise/web.el/src/org/netbeans/modules/web/el/spi/ResourceBundle.java +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/spi/ResourceBundle.java @@ -25,23 +25,23 @@ * @author marekfukala */ public final class ResourceBundle { - + private final String baseName, var; private final List files; - + public ResourceBundle(String baseName, String var, List files) { this.baseName = baseName; this.var = var; this.files = files; } - + /** * @return fully qualified name of the properties file representing the resource bundle. */ public String getBaseName() { return baseName; } - + /** * @return variable representing the resource bundle */ @@ -56,4 +56,9 @@ public List getFiles() { return files; } + @Override + public String toString() { + return "ResourceBundle{" + "baseName=" + baseName + ", var=" + var + '}'; + } + } diff --git a/enterprise/web.jsf/src/org/netbeans/modules/web/jsf/api/editor/JSFResourceBundlesProvider.java b/enterprise/web.jsf/src/org/netbeans/modules/web/jsf/api/editor/JSFResourceBundlesProvider.java index bccd8a430f8b..33bac43df6d4 100644 --- a/enterprise/web.jsf/src/org/netbeans/modules/web/jsf/api/editor/JSFResourceBundlesProvider.java +++ b/enterprise/web.jsf/src/org/netbeans/modules/web/jsf/api/editor/JSFResourceBundlesProvider.java @@ -22,9 +22,12 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.java.project.JavaProjectConstants; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectUtils; @@ -53,29 +56,43 @@ public static List getResourceBundles(final Project project) { try { return model.runReadAction(metadata -> { List applications = metadata.getElements(Application.class); + + if (applications.isEmpty()) { + return Collections.emptyList(); + } + + List allSourceGroups = new ArrayList<>(); + Collections.addAll(allSourceGroups, SourceGroups.getJavaSourceGroups(project)); + Collections.addAll(allSourceGroups, ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_RESOURCES)); + + ClassPath compileClassPath = null; + if (!allSourceGroups.isEmpty()) { + compileClassPath = ClassPath.getClassPath(allSourceGroups.get(0).getRootFolder(), ClassPath.COMPILE); + } + List result = new ArrayList<>(); for (Application application : applications) { for (org.netbeans.modules.web.jsf.api.facesmodel.ResourceBundle bundle : application.getResourceBundles()) { - if (bundle.getBaseName() == null) { + String baseName = bundle.getBaseName(); + if (baseName == null) { continue; } - List files = new ArrayList<>(); - // java source source groups - for (SourceGroup sourceGroup : SourceGroups.getJavaSourceGroups(project)) { - FileObject bundleFile = getBundleFileInSourceGroup(sourceGroup, bundle); - if (bundleFile != null) { - files.add(bundleFile); - } + Set fileSet = new LinkedHashSet<>(); + int lastDelim = baseName.lastIndexOf("."); + String bundleSimpleName = (lastDelim <= 0) ? baseName : baseName.substring(lastDelim + 1); + String packagePath = (lastDelim <= 0) ? "" : baseName.replace(".", "/").substring(0, lastDelim); + + for (SourceGroup sourceGroup : allSourceGroups) { + FileObject root = sourceGroup.getRootFolder(); + FileObject folder = (lastDelim <= 0) ? root : root.getFileObject(packagePath); + addBundleFilesInFolder(fileSet, folder, bundleSimpleName); } - // resource source groups - for (SourceGroup sourceGroup : ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_RESOURCES)) { - FileObject bundleFile = getBundleFileInSourceGroup(sourceGroup, bundle); - if (bundleFile != null) { - files.add(bundleFile); - } + + if (fileSet.isEmpty() && compileClassPath != null) { + addBundleFilesInCompileClasspath(fileSet, compileClassPath, bundleSimpleName, packagePath); } - - result.add(new ResourceBundle(bundle.getBaseName(), bundle.getVar(), files)); + + result.add(new ResourceBundle(bundle.getBaseName(), bundle.getVar(), new ArrayList<>(fileSet))); } } return result; @@ -86,28 +103,22 @@ public static List getResourceBundles(final Project project) { return Collections.emptyList(); } - private static FileObject getBundleFileInSourceGroup(SourceGroup sourceGroup, org.netbeans.modules.web.jsf.api.facesmodel.ResourceBundle bundle) { - int lastDelim = bundle.getBaseName().lastIndexOf("/"); //NOI18N - String bundleName = bundle.getBaseName().substring(lastDelim + 1); - if (lastDelim <= 0) { - // in root folder or default package - return getBundleInFolder(sourceGroup.getRootFolder(), bundleName); - } else { - // in the subfolder or non-default package - String parentFolder = bundle.getBaseName().replace(".", "/").substring(0, lastDelim); //NOI18N - return getBundleInFolder(sourceGroup.getRootFolder().getFileObject(parentFolder), bundleName); - } - } - - private static FileObject getBundleInFolder(FileObject folder, String bundleName) { + private static void addBundleFilesInFolder(Set files, FileObject folder, String bundleName) { if (folder != null && folder.isValid() && folder.isFolder()) { for (FileObject fo : folder.getChildren()) { - if (fo.getName().startsWith(bundleName) && "properties".equals(fo.getExt())) { //NOI18N - return fo; + if ("properties".equals(fo.getExt()) + && (fo.getName().equals(bundleName) || fo.getName().startsWith(bundleName + "_"))) { + files.add(fo); } } } - return null; + } + + private static void addBundleFilesInCompileClasspath(Set files, ClassPath cp, String bundleName, String packagePath) { + List folders = cp.findAllResources(packagePath); + for (FileObject folder : folders) { + addBundleFilesInFolder(files, folder, bundleName); + } } } From 0f34d2b3be4aee3ccc96b3667a1b35a361605eab Mon Sep 17 00:00:00 2001 From: Nicola Isotta Date: Fri, 30 Jan 2026 18:01:21 +0100 Subject: [PATCH 2/4] Improve declarations popup look --- .../modules/web/el/ResourceBundles.java | 12 +++++- .../el/navigation/ELDeclarationFinder.java | 37 ++++++++++--------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/ResourceBundles.java b/enterprise/web.el/src/org/netbeans/modules/web/el/ResourceBundles.java index c24bd83873d4..5ff1f04ca8cf 100644 --- a/enterprise/web.el/src/org/netbeans/modules/web/el/ResourceBundles.java +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/ResourceBundles.java @@ -248,7 +248,7 @@ public List getLocationsForBundleKey(ResolverContext context, String i try { StyledDocument document = ec.getDocument(); int offset = document.getText(0, document.getLength()).indexOf(line.getText()); - locations.add(new Location(offset, location.getFile())); + locations.add(new Location(offset, location.getFile(), line.getLineNumber())); break; } catch (BadLocationException ex) { Exceptions.printStackTrace(ex); @@ -642,14 +642,20 @@ public static class Location { private final int offset; private final FileObject file; + private final int lineNumber; public Location(FileObject file) { this(0, file); } public Location(int offset, FileObject file) { + this(offset, file, -1); + } + + public Location(int offset, FileObject file, int lineNumber) { this.offset = offset; this.file = file; + this.lineNumber = lineNumber; } public int getOffset() { @@ -659,5 +665,9 @@ public int getOffset() { public FileObject getFile() { return file; } + + public int getLineNumber() { + return lineNumber; + } } } diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELDeclarationFinder.java b/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELDeclarationFinder.java index 177d7548595a..9305b7b24256 100644 --- a/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELDeclarationFinder.java +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/navigation/ELDeclarationFinder.java @@ -101,7 +101,7 @@ public void run(CompilationController cc) throws Exception { refs.offset = bundleLocations.get(0).offset; refs.elementHandle = bundleLocations.get(0).elementHandle; for (RefsHolder location : bundleLocations) { - alternatives.add(new ResourceBundleAlternative(location.fo, location.offset)); + alternatives.add(new ResourceBundleAlternative(location.fo, location.offset, location.lineNumber)); } } } @@ -176,7 +176,7 @@ private static List getBundleLocations(CompilationContext ccontext, value = null; } return locations.stream().map(location -> new RefsHolder(location.getFile(), location.getOffset(), - ELCompletionUtil.getResourceBundleElementHandle(key, value, nodeElem.second(), location.getFile()))).toList(); + ELCompletionUtil.getResourceBundleElementHandle(key, value, nodeElem.second(), location.getFile()), location.getLineNumber())).toList(); } } } @@ -187,10 +187,12 @@ private static class ResourceBundleAlternative implements AlternativeLocation { private final FileObject file; private final int offset; + private final int lineNumber; - public ResourceBundleAlternative(FileObject file, int offset) { + public ResourceBundleAlternative(FileObject file, int offset, int lineNumber) { this.offset = offset; this.file = file; + this.lineNumber = lineNumber; } @Override @@ -200,13 +202,10 @@ public org.netbeans.modules.csl.api.ElementHandle getElement() { @Override public String getDisplayHtml(HtmlFormatter formatter) { - StringBuilder b = new StringBuilder(); - - b.append("");//NOI18N - b.append(""); //NOI18N - b.append(file.getName()); - b.append(""); //NOI18N - b.append(" in "); //NOI18N + formatter.emphasis(true); + formatter.appendText(file.getName()); + formatter.emphasis(false); + formatter.appendText(" in "); //add a link to the file relative to the web root FileObject pathRoot = ProjectWebRootQuery.getWebRoot(file); @@ -226,14 +225,14 @@ public String getDisplayHtml(HtmlFormatter formatter) { path = file.getPath(); } - b.append(""); //NOI18N - b.append(path); - b.append(""); //NOI18N - if (offset > 0) { - b.append(":"); //NOI18N - b.append(offset + 1); //line offsets are counted from zero, but in editor lines starts with one. + formatter.appendHtml(""); + formatter.appendText(path); + formatter.appendHtml(""); + if (lineNumber > -1) { + formatter.appendText(":"); //NOI18N + formatter.appendText(String.valueOf(lineNumber + 1)); //line numbers are counted from zero, but in editor lines starts with one. } - return b.toString(); + return formatter.getText(); } @Override @@ -288,6 +287,7 @@ private static class RefsHolder { ElementHandle handle; FileObject fo; int offset = -1; + int lineNumber = -1; org.netbeans.modules.csl.api.ElementHandle elementHandle; RefsHolder() { @@ -298,10 +298,11 @@ private static class RefsHolder { this.offset = offset; } - public RefsHolder(FileObject fo, int offset, org.netbeans.modules.csl.api.ElementHandle elementHandle) { + public RefsHolder(FileObject fo, int offset, org.netbeans.modules.csl.api.ElementHandle elementHandle, int lineNumber) { this.fo = fo; this.offset = offset; this.elementHandle = elementHandle; + this.lineNumber = lineNumber; } } From c494408e122f7115dbdc15f26932c61665260cb7 Mon Sep 17 00:00:00 2001 From: Nicola Isotta Date: Wed, 4 Feb 2026 18:19:38 +0100 Subject: [PATCH 3/4] new svg icons --- .../el/completion/resources/propertiesKey.svg | 51 ++++++++++++++++++ .../completion/resources/propertiesLocale.svg | 54 +++++++++++++++++++ .../modules/properties/propertiesKey.svg | 51 ++++++++++++++++++ .../modules/properties/propertiesLocale.svg | 54 +++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 enterprise/web.el/src/org/netbeans/modules/web/el/completion/resources/propertiesKey.svg create mode 100644 enterprise/web.el/src/org/netbeans/modules/web/el/completion/resources/propertiesLocale.svg create mode 100644 ide/properties/src/org/netbeans/modules/properties/propertiesKey.svg create mode 100644 ide/properties/src/org/netbeans/modules/properties/propertiesLocale.svg diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/completion/resources/propertiesKey.svg b/enterprise/web.el/src/org/netbeans/modules/web/el/completion/resources/propertiesKey.svg new file mode 100644 index 000000000000..b81160dfce5e --- /dev/null +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/completion/resources/propertiesKey.svg @@ -0,0 +1,51 @@ + + + + + + α + + + a + + + \ No newline at end of file diff --git a/enterprise/web.el/src/org/netbeans/modules/web/el/completion/resources/propertiesLocale.svg b/enterprise/web.el/src/org/netbeans/modules/web/el/completion/resources/propertiesLocale.svg new file mode 100644 index 000000000000..e8702b9da09c --- /dev/null +++ b/enterprise/web.el/src/org/netbeans/modules/web/el/completion/resources/propertiesLocale.svg @@ -0,0 +1,54 @@ + + + + + + α + + + a + + + + \ No newline at end of file diff --git a/ide/properties/src/org/netbeans/modules/properties/propertiesKey.svg b/ide/properties/src/org/netbeans/modules/properties/propertiesKey.svg new file mode 100644 index 000000000000..b81160dfce5e --- /dev/null +++ b/ide/properties/src/org/netbeans/modules/properties/propertiesKey.svg @@ -0,0 +1,51 @@ + + + + + + α + + + a + + + \ No newline at end of file diff --git a/ide/properties/src/org/netbeans/modules/properties/propertiesLocale.svg b/ide/properties/src/org/netbeans/modules/properties/propertiesLocale.svg new file mode 100644 index 000000000000..e8702b9da09c --- /dev/null +++ b/ide/properties/src/org/netbeans/modules/properties/propertiesLocale.svg @@ -0,0 +1,54 @@ + + + + + + α + + + a + + + + \ No newline at end of file From 0764ba53c93888ccdaa6191071166ed93a2bca12 Mon Sep 17 00:00:00 2001 From: Nicola Isotta Date: Thu, 5 Feb 2026 20:57:37 +0100 Subject: [PATCH 4/4] new svg icons --- .../jsf/editor/resources/propertiesKey.svg | 51 ++++++++++++++++++ .../jsf/editor/resources/propertiesLocale.svg | 54 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/resources/propertiesKey.svg create mode 100644 enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/resources/propertiesLocale.svg diff --git a/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/resources/propertiesKey.svg b/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/resources/propertiesKey.svg new file mode 100644 index 000000000000..b81160dfce5e --- /dev/null +++ b/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/resources/propertiesKey.svg @@ -0,0 +1,51 @@ + + + + + + α + + + a + + + \ No newline at end of file diff --git a/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/resources/propertiesLocale.svg b/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/resources/propertiesLocale.svg new file mode 100644 index 000000000000..e8702b9da09c --- /dev/null +++ b/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/resources/propertiesLocale.svg @@ -0,0 +1,54 @@ + + + + + + α + + + a + + + + \ No newline at end of file