From 4b30c1a2cff4ee4a615bc0940327035705198cbc Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Wed, 25 Jul 2012 16:43:33 -0700 Subject: [PATCH] Initial commit --- META-INF/plugin.xml | 22 + src/abbreviation.properties | 115 +++++ .../AbbreviationUsage.html | 6 + .../abbrev/AbbreviationInspection.java | 453 ++++++++++++++++++ .../AbbreviationInspectionProvider.java | 24 + .../inspection/abbrev/AbbreviationRules.java | 299 ++++++++++++ 6 files changed, 919 insertions(+) create mode 100644 META-INF/plugin.xml create mode 100644 src/abbreviation.properties create mode 100644 src/inspectionDescriptions/AbbreviationUsage.html create mode 100644 src/org/gridgain/inspection/abbrev/AbbreviationInspection.java create mode 100644 src/org/gridgain/inspection/abbrev/AbbreviationInspectionProvider.java create mode 100644 src/org/gridgain/inspection/abbrev/AbbreviationRules.java diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml new file mode 100644 index 0000000..7ef52b4 --- /dev/null +++ b/META-INF/plugin.xml @@ -0,0 +1,22 @@ + + GridGain abbreviation rules + This plugin checks for incorrect usage of abbreviated names + 1.0 + GridGain Inc. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/abbreviation.properties b/src/abbreviation.properties new file mode 100644 index 0000000..6723049 --- /dev/null +++ b/src/abbreviation.properties @@ -0,0 +1,115 @@ +address=addr +administration=admin +argument=arg +array=arr +attachment=attach +attributes=attrs +buffer=buf +certificate=cert +callable=call +char=c +channel=ch +class=cls +closure=c +collection=col +connection=conn +command=cmd +communication=comm +comparator=comp +condition=cond +config=cfg +context=ctx +control=ctrl +coordinator=crd +copy=cp +counter=cntr +count=cnt +current=curr +database=db +declare=decl +declaration=decl +default=dflt +delete=del +delimiter=delim +description=desc +descriptor=descr +destination=dest +directory=dir +event=evt +exception=e +execute=exec +expected=exp +externalizable=ext +frequency=freq +future=fut +group=grp +handler=hnd +header=hdr +implementation=impl +index=idx +initial=init +initialize=init +interface=itf +iterator=iter +listener=lsnr +local=loc +locale=loc +logger=log +loader=ldr +manager=mgr +message=msg +method=mtd +microkernel=mk +milliseconds=ms +multicast=mcast +mutex=mux +network=net +number=num +object=obj +package=pkg +parameter=param +permission=perm +permissions=perms +password=pwd +pattern=ptrn +policy=plc +predicate=pred +priority=pri +projection=prj +projections=prjs +property=prop +properties=props +protocol=proto +process=proc +query=qry +receive=rcv +recipient=rcpt +reference=ref +remove=rmv +removed=rmv +rename=ren +repository=repo +request=req +resource=rsrc +response=res +send=snd +sender=snd +serializable=ser +service=srvc +session=ses +sequence=seq +sibling=sib +socket=sock +source=src +specification=spec +strategy=stgy +string=str +system=sys +taxonomy=tax +timestamp=ts +token=tok +topology=top +unicast=ucast +value=val +version=ver +windows=win diff --git a/src/inspectionDescriptions/AbbreviationUsage.html b/src/inspectionDescriptions/AbbreviationUsage.html new file mode 100644 index 0000000..bd0a46d --- /dev/null +++ b/src/inspectionDescriptions/AbbreviationUsage.html @@ -0,0 +1,6 @@ + + +Highlights variables and fields with names where +abbreviations should be used instead of full words (e.g. cnt should be used instead of count) + + \ No newline at end of file diff --git a/src/org/gridgain/inspection/abbrev/AbbreviationInspection.java b/src/org/gridgain/inspection/abbrev/AbbreviationInspection.java new file mode 100644 index 0000000..efeb94b --- /dev/null +++ b/src/org/gridgain/inspection/abbrev/AbbreviationInspection.java @@ -0,0 +1,453 @@ +// @java.file.header + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ +package org.gridgain.inspection.abbrev; + +import com.intellij.codeInsight.daemon.*; +import com.intellij.codeInspection.*; +import com.intellij.openapi.project.*; +import com.intellij.psi.*; +import com.intellij.refactoring.*; +import org.jetbrains.annotations.*; + +import java.io.*; +import java.util.*; + +/** + * Inspection that checks variable names for usage of restricted words that + * need to be abbreviated. + * + * @author @java.author + * @version @java.version + */ +public class AbbreviationInspection extends BaseJavaLocalInspectionTool { + /** Abbreviation rules. */ + private AbbreviationRules abbreviationRules; + + /** User message for options panel. */ + private String userMsg; + + /** + * Constructor. + */ + public AbbreviationInspection() { + String ggHome = System.getenv("GRIDGAIN_HOME"); + + if (ggHome == null) { + userMsg = "GRIDGAIN_HOME environment variable was not found. Using hard-coded abbreviation table."; + + abbreviationRules = AbbreviationRules.getInstance(null); + } + else { + File abbrevFile = new File(new File(ggHome), "idea" + File.separatorChar + "abbreviation.properties"); + + if (!abbrevFile.exists() || !abbrevFile.isFile()) { + userMsg = "${GRIDGAIN_HOME}/idea/abbreviation.properties was not found. Using hard-coded " + + "abbreviation table."; + + abbreviationRules = AbbreviationRules.getInstance(null); + } + else { + userMsg = "Using " + abbrevFile.getAbsolutePath() + " as abbreviation rules file."; + + abbreviationRules = AbbreviationRules.getInstance(abbrevFile); + } + } + } + + /** {@inheritDoc} */ + @NotNull @Override public String getGroupDisplayName() { + return GroupNames.STYLE_GROUP_NAME; + } + + /** {@inheritDoc} */ + @NotNull @Override public String getDisplayName() { + return "Incorrect abbreviation usage"; + } + + /** {@inheritDoc} */ + @NotNull @Override public String getShortName() { + return "AbbreviationUsage"; + } + + /** {@inheritDoc} */ + @NotNull @Override public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, + final boolean isOnTheFly) { + return new JavaElementVisitor() { + /** {@inheritDoc} */ + @Override public void visitField(PsiField field) { + boolean isFinal = false; + + boolean isStatic = false; + + if (field instanceof PsiEnumConstant) + return; + + for (PsiElement el : field.getChildren()) { + if (el instanceof PsiModifierList) { + PsiModifierList modifiers = (PsiModifierList)el; + + isFinal = modifiers.hasExplicitModifier("final"); + + isStatic = modifiers.hasExplicitModifier("static"); + } + + if (el instanceof PsiIdentifier && !(isFinal && isStatic)) + checkShouldAbbreviate(field, el); + } + } + + /** {@inheritDoc} */ + @Override public void visitLocalVariable(PsiLocalVariable variable) { + for (PsiElement el : variable.getChildren()) { + if (el instanceof PsiIdentifier) + checkShouldAbbreviate(variable, el); + } + } + + /** {@inheritDoc} */ + @Override public void visitMethod(PsiMethod mtd) { + for (PsiParameter par : mtd.getParameterList().getParameters()) { + for (PsiElement el : par.getChildren()) { + if (el instanceof PsiIdentifier) + checkShouldAbbreviate(par, el); + } + } + } + + /** + * Checks if given name contains a part that should be abbreviated and registers fix if needed. + * + * @param toCheck Element to check and rename. + * @param el Element to highlight the problem. + */ + private void checkShouldAbbreviate(PsiNamedElement toCheck, PsiElement el) { + List nameParts = nameParts(toCheck.getName()); + + for (String part : nameParts) { + if (getAbbreviation(part) != null) { + holder.registerProblem(el, "Abbreviation should be used", + new RenameToFix(replaceWithAbbreviations(nameParts))); + + break; + } + } + } + }; + } + + /** + * Creates panel with user message. + * + * @return Panel instance. + */ + @Override public javax.swing.JComponent createOptionsPanel() { + javax.swing.JPanel panel = new javax.swing.JPanel(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT)); + + javax.swing.JLabel msg = new javax.swing.JLabel(userMsg); + + panel.add(msg); + + return panel; + } + + /** + * Renames variable to a given name. + */ + private class RenameToFix implements LocalQuickFix { + /** New proposed variable name. */ + private String name; + + /** + * Creates rename to fix. + * + * @param name New variable name. + */ + private RenameToFix(@NotNull String name) { + this.name = name; + } + + /** {@inheritDoc} */ + @NotNull public String getName() { + return "Rename to " + name; + } + + /** {@inheritDoc} */ + @NotNull public String getFamilyName() { + return ""; + } + + /** {@inheritDoc} */ + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descr) { + PsiElement element = descr.getPsiElement().getParent(); + + rename(project, element, name); + } + + /** + * Renames given element to a given name. + * + * @param project Project in which variable is located. + * @param element Element to rename. + * @param name New element name. + */ + private void rename(@NotNull Project project, @NotNull PsiElement element, @NotNull String name) { + JavaRefactoringFactory factory = JavaRefactoringFactory.getInstance(project); + + RenameRefactoring renRefactoring = factory.createRename(element, name, false, false); + + renRefactoring.run(); + } + } + + /** + * Constructs abbreviated name from parts of wrong name. + * + * @param oldNameParts Split of variable name. + * @return Abbreviated variable name. + */ + private String replaceWithAbbreviations(List oldNameParts) { + StringBuilder sb = new StringBuilder(); + + for (String part : oldNameParts) { + String abbrev = getAbbreviation(part); + + if (abbrev == null) + sb.append(part); + else { + // Only the following cases are possible: count, Count and COUNT since + // parser splits tokens based on this rule. + int pos = sb.length(); + + sb.append(abbrev); + + if (Character.isUpperCase(part.charAt(0))) { + sb.setCharAt(pos, Character.toUpperCase(sb.charAt(pos))); + + pos++; + + if (Character.isUpperCase(part.charAt(part.length() - 1))) { + // Full abbreviation, like COUNT + while (pos < sb.length()) { + sb.setCharAt(pos, Character.toUpperCase(sb.charAt(pos))); + + pos++; + } + } + } + } + } + + return sb.toString(); + } + + /** + * Performs lookup of name part in abbreviation table. + * + * @param namePart Name part to lookup. + * @return Abbreviation for given name or {@code null} if there is no such abbreviation. + */ + @Nullable private String getAbbreviation(String namePart) { + return abbreviationRules.getAbbreviation(namePart); + } + + /** + * Enum represents state of variable name parser. + */ + private enum ParserState { + /** State when no input symbols parsed yet. */ + START, + + /** First symbol parsed was capital. */ + CAPITAL, + + /** Parser is inside word token. */ + WORD, + + /** Parser is inside number. */ + NUM, + + /** Parser is inside abbreviation in capital letters. */ + ABBREVIATION + } + + /** + * Splits variable name into parts according to java naming conventions. + * + * @param name Variable or field name. + * @return List containing variable name parts. + */ + List nameParts(String name) { + List res = new LinkedList(); + + StringBuilder sb = new StringBuilder(); + + ParserState state = ParserState.START; + + char pending = 0; + + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + + switch (state) { + case START: + sb.append(c); + + if (Character.isLowerCase(c)) + state = ParserState.WORD; + else if (Character.isUpperCase(c)) + state = ParserState.CAPITAL; + else if (Character.isDigit(c)) + state = ParserState.NUM; + else { + res.add(sb.toString()); + + sb.setLength(0); + + // Remain in start state. + } + break; + + case CAPITAL: + if (Character.isLowerCase(c)) { + sb.append(c); + + state = ParserState.WORD; + } + else if (Character.isUpperCase(c)) { + pending = c; + + state = ParserState.ABBREVIATION; + } + else if (Character.isDigit(c)) { + res.add(sb.toString()); + + sb.setLength(0); + + sb.append(c); + + state = ParserState.NUM; + } + else { + res.add(sb.toString()); + + sb.setLength(0); + + res.add(String.valueOf(c)); + + state = ParserState.START; + } + break; + + case WORD: + if (Character.isLowerCase(c)) + sb.append(c); + else { + res.add(sb.toString()); + + sb.setLength(0); + + state = ParserState.START; + + // Unread. + i--; + } + break; + + case ABBREVIATION: + if (Character.isUpperCase(c)) { + sb.append(pending); + + pending = c; + } + else if (Character.isLowerCase(c)) { + res.add(sb.toString()); + + sb.setLength(0); + + sb.append(pending).append(c); + + state = ParserState.WORD; + } + else { + sb.append(pending); + + res.add(sb.toString()); + + sb.setLength(0); + + state = ParserState.START; + + // Unread. + i--; + } + break; + + case NUM: + if (Character.isDigit(c)) + sb.append(c); + else { + res.add(sb.toString()); + + sb.setLength(0); + + state = ParserState.START; + + // Unread. + i--; + } + break; + } + } + + if (state == ParserState.ABBREVIATION) + sb.append(pending); + + if (sb.length() > 0) + res.add(sb.toString()); + + return res; + } + + public static void main(String[] args) { + AbbreviationInspection i = new AbbreviationInspection(); + + assert listsEqual(Arrays.asList("count"), i.nameParts("count")); + assert listsEqual(Arrays.asList("Count"), i.nameParts("Count")); + assert listsEqual(Arrays.asList("Count", "1"), i.nameParts("Count1")); + assert listsEqual(Arrays.asList("my", "Count"), i.nameParts("myCount")); + assert listsEqual(Arrays.asList("my", "Count"), i.nameParts("myCount")); + assert listsEqual(Arrays.asList("MY", "_", "COUNT"), i.nameParts("MY_COUNT")); + assert listsEqual(Arrays.asList("MY", "_", "COUNT", "1"), i.nameParts("MY_COUNT1")); + assert listsEqual(Arrays.asList("_", "_", "my", "_", "Count"), i.nameParts("__my_Count")); + assert listsEqual(Arrays.asList("my", "123", "Count"), i.nameParts("my123Count")); + assert listsEqual(Arrays.asList("my", "_","123", "_", "Count"), i.nameParts("my_123_Count")); + assert listsEqual(Arrays.asList("my","BIG", "Count"), i.nameParts("myBIGCount")); + assert listsEqual(Arrays.asList("my","BIG", "_", "count"), i.nameParts("myBIG_count")); + assert listsEqual(Arrays.asList("my","1", "BIG", "2", "count"), i.nameParts("my1BIG2count")); + assert listsEqual(Arrays.asList("my","1", "BIG", "2", "Count"), i.nameParts("my1BIG2Count")); + + + assert "cnt".equals(i.replaceWithAbbreviations(i.nameParts("count"))); + assert "Cnt".equals(i.replaceWithAbbreviations(i.nameParts("Count"))); + assert "myCnt".equals(i.replaceWithAbbreviations(i.nameParts("myCount"))); + assert "MY_CNT".equals(i.replaceWithAbbreviations(i.nameParts("MY_COUNT"))); + } + + private static boolean listsEqual(List one, List two) { + if (one.size() != two.size()) + return false; + + for (int i = 0; i < one.size(); i++) { + if (!one.get(i).equals(two.get(i))) + return false; + } + + return true; + } +} diff --git a/src/org/gridgain/inspection/abbrev/AbbreviationInspectionProvider.java b/src/org/gridgain/inspection/abbrev/AbbreviationInspectionProvider.java new file mode 100644 index 0000000..870ad38 --- /dev/null +++ b/src/org/gridgain/inspection/abbrev/AbbreviationInspectionProvider.java @@ -0,0 +1,24 @@ +// @java.file.header + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.inspection.abbrev; + +import com.intellij.codeInspection.*; + +/** + * Provider for inspections. + * + * @author @java.author + * @version @java.version + */ +public class AbbreviationInspectionProvider implements InspectionToolProvider { + public Class[] getInspectionClasses() { + return new Class[] {AbbreviationInspection.class}; + } +} diff --git a/src/org/gridgain/inspection/abbrev/AbbreviationRules.java b/src/org/gridgain/inspection/abbrev/AbbreviationRules.java new file mode 100644 index 0000000..10b109a --- /dev/null +++ b/src/org/gridgain/inspection/abbrev/AbbreviationRules.java @@ -0,0 +1,299 @@ +// @java.file.header + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.inspection.abbrev; + +import org.jetbrains.annotations.*; + +import java.io.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; + +/** + * Abbreviation rules container. + * + * @author @java.author + * @version @java.version + */ +public class AbbreviationRules { + /** File refresh frequency. */ + private static final int FILE_REFRESH_FREQ = 5000; + + /** Hardcoded abbreviation table if no abbreviation file can be found. */ + private static final String[][] ABBREV_TABLE = { + {"address", "addr"}, + {"administration", "admin"}, + {"argument", "arg"}, + {"array", "arr"}, + {"attachment", "attach"}, + {"attributes", "attrs"}, + {"buffer", "buf"}, + {"certificate", "cert"}, + {"callable", "call"}, + {"char", "c"}, + {"channel", "ch"}, + {"class", "cls"}, + {"closure", "c"}, + {"collection", "col"}, + {"connection", "conn"}, + {"command", "cmd"}, + {"communication", "comm"}, + {"comparator", "comp"}, + {"condition", "cond"}, + {"config", "cfg"}, + {"context", "ctx"}, + {"control", "ctrl"}, + {"coordinator", "crd"}, + {"copy", "cp"}, + {"counter", "cntr"}, + {"count", "cnt"}, + {"current", "curr"}, + {"database", "db"}, + {"declare", "decl"}, + {"declaration", "decl"}, + {"default", "dflt"}, + {"delete", "del"}, + {"delimiter", "delim"}, + {"description", "desc"}, + {"descriptor", "descr"}, + {"destination", "dest"}, + {"directory", "dir"}, + {"event", "evt"}, + {"exception", "e"}, + {"execute", "exec"}, + {"expected", "exp"}, + {"externalizable", "ext"}, + {"frequency", "freq"}, + {"future", "fut"}, + {"group", "grp"}, + {"handler", "hnd"}, + {"header", "hdr"}, + {"implementation", "impl"}, + {"index", "idx"}, + {"initial", "init"}, + {"initialize", "init"}, + {"interface", "itf"}, + {"iterator", "iter"}, + {"listener", "lsnr"}, + {"local", "loc"}, + {"locale", "loc"}, + {"logger", "log"}, + {"loader", "ldr"}, + {"manager", "mgr"}, + {"message", "msg"}, + {"method", "mtd"}, + {"microkernel", "mk"}, + {"milliseconds", "ms"}, + {"multicast", "mcast"}, + {"mutex", "mux"}, + {"network", "net"}, + {"number", "num"}, + {"object", "obj"}, + {"package", "pkg"}, + {"parameter", "param"}, + {"permission", "perm"}, + {"permissions", "perms"}, + {"password", "pwd"}, + {"pattern", "ptrn"}, + {"policy", "plc"}, + {"predicate", "pred"}, + {"priority", "pri"}, + {"projection", "prj"}, + {"projections", "prjs"}, + {"property", "prop"}, + {"properties", "props"}, + {"protocol", "proto"}, + {"process", "proc"}, + {"query", "qry"}, + {"receive", "rcv"}, + {"recipient", "rcpt"}, + {"reference", "ref"}, + {"remove", "rmv"}, + {"removed", "rmv"}, + {"rename", "ren"}, + {"repository", "repo"}, + {"request", "req"}, + {"resource", "rsrc"}, + {"response", "res"}, + {"send", "snd"}, + {"sender", "snd"}, + {"serializable", "ser"}, + {"service", "srvc"}, + {"session", "ses"}, + {"sequence", "seq"}, + {"sibling", "sib"}, + {"socket", "sock"}, + {"source", "src"}, + {"specification", "spec"}, + {"strategy", "stgy"}, + {"string", "str"}, + {"system", "sys"}, + {"taxonomy", "tax"}, + {"timestamp", "ts"}, + {"token", "tok"}, + {"topology", "top"}, + {"unicast", "ucast"}, + {"value", "val"}, + {"version", "ver"}, + {"windows", "win"}, + }; + + /** Map from common words to abbreviations. */ + private volatile Map abbreviationMap = new ConcurrentHashMap(); + + /** File with abbreviation rules. */ + private File abbreviationFile; + + /** Initialization latch. */ + private CountDownLatch initLatch = new CountDownLatch(1); + + /** Singleton instance. */ + private static volatile AbbreviationRules instance; + + /** Init flag */ + private static AtomicBoolean initFlag = new AtomicBoolean(); + + /** Singleton init latch. */ + private static CountDownLatch singletonInitLatch = new CountDownLatch(1); + + /** + * Singleton factory. + * + * @param file Abbreviations file. + * @return Instance of abbreviation rules. + */ + public static AbbreviationRules getInstance(@Nullable File file) { + try { + if (initFlag.compareAndSet(false, true)) { + instance = new AbbreviationRules(file); + + singletonInitLatch.countDown(); + } + else + singletonInitLatch.await(); + + return instance; + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + + throw new IllegalStateException("Interrupted while waiting for instance initialization"); + } + } + + /** + * Creates abbreviation rules depending on whether rules file found or not. + * + * @param file Abbreviations file or {@code null} if internal rules should be used. + */ + private AbbreviationRules(File file) { + if (file == null) { + for (String[] entry : ABBREV_TABLE) { + assert entry.length == 2; + + abbreviationMap.put(entry[0], entry[1]); + } + + initLatch.countDown(); + } + else { + abbreviationFile = file; + + Thread fileWatchThread = new Thread(new AbbreviationFileWatcher()); + + fileWatchThread.setDaemon(true); + + fileWatchThread.start(); + } + } + + /** + * Performs lookup of name part in abbreviation table. + * + * @param namePart Name part to lookup. + * @return Abbreviation for given name or {@code null} if there is no such abbreviation. + */ + @Nullable public String getAbbreviation(String namePart) { + try { + if (initLatch.getCount() == 1) + initLatch.await(); + + return abbreviationMap.get(namePart.toLowerCase()); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + + return null; + } + } + + /** + * Thread that watches for abbreviation file changes. + */ + private class AbbreviationFileWatcher implements Runnable { + /** File load timestamp. */ + private long loadTs; + + @Override public void run() { + try { + while (!Thread.currentThread().isInterrupted()) { + loadAbbreviations(); + + if (initLatch.getCount() == 1) + initLatch.countDown(); + + Thread.sleep(FILE_REFRESH_FREQ); + } + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + /** + * Checks for file modification timestamp and refreshes abbreviation rules if needed. + */ + @SuppressWarnings("unchecked") + private void loadAbbreviations() { + if (abbreviationFile.lastModified() > loadTs) { + loadTs = abbreviationFile.lastModified(); + + InputStream input = null; + + try { + input = new FileInputStream(abbreviationFile); + + Properties props = new Properties(); + + props.load(new BufferedReader(new InputStreamReader(input))); + + Map refreshed = new ConcurrentHashMap(); + + for (Map.Entry entry : props.entrySet()) + refreshed.put((String)entry.getKey(), (String)entry.getValue()); + + abbreviationMap = refreshed; + } + catch (IOException ignored) { + // Just leave the last state. + } + finally { + if (input != null) { + try { + input.close(); + } + catch (IOException ignored) { + } + } + } + } + } + } +} -- 2.25.1