At first, some context: all the code inserted below is within another class declared as the public class TheClass extends SomeProprietaryClass . I cannot declare these classes in another file for various reasons ... And the log messages are in French. And I'm the "last happy" programmer. At the heart of this problem is the problem ...
Now the code ... (maybe too much of this - shutdown on demand only to save the corresponding parts)
Custom exception:
private static final class BreadCrumbException extends Exception { private BreadCrumbException(final String message) { super(message); } private BreadCrumbException(final String message, final Throwable cause) { super(message, cause); } }
The enumeration for "materializing" the visibility of a member of a bundle:
private enum Visibility { MAINPAGE("R"), MENU("M"), BREADCRUMB("A"), COMMERCIAL("C"); private static final Map<String, Visibility> reverseMap = new HashMap<String, Visibility>(); private static final String characterClass; static { final StringBuilder sb = new StringBuilder("["); for (final Visibility v: values()) { reverseMap.put(v.flag, v); sb.append(v.flag); } sb.append("]"); characterClass = sb.toString(); } private final String flag; Visibility(final String flag) { this.flag = flag; } static EnumSet<Visibility> fromBC(final String element) { final EnumSet<Visibility> result = EnumSet.noneOf(Visibility.class); for (final String s: reverseMap.keySet()) if (element.contains(s)) result.add(reverseMap.get(s)); return result; } static String asCharacterClass() { return characterClass; } static String asString(final EnumSet<Visibility> set) { final StringBuilder sb = new StringBuilder(); for (final Visibility v: set) sb.append(v.flag); return sb.toString(); } @Override public String toString() { return flag; } }
Palette Element Element:
private static class BreadCrumbElement { private static final Pattern p = Pattern.compile(String.format("(%s+)(\\d+)", Visibility.asCharacterClass())); private final String element; private final String menuID; private final EnumSet<Visibility> visibility; BreadCrumbElement(final String element) { final Matcher m = p.matcher(element); if (!m.matches()) throw new IllegalArgumentException("Élément de fil d'ariane invalide: " + element); this.element = element; visibility = EnumSet.copyOf(Visibility.fromBC(m.group(1))); menuID = m.group(2); } public boolean visibleFrom(final Visibility v) { return visibility.contains(v); } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final BreadCrumbElement that = (BreadCrumbElement) o; return element.equals(that.element); } @Override public int hashCode() { return element.hashCode(); } @Override public String toString() { return element; } public String getMenuID() { return menuID; } }
Rusk:
private static class BreadCrumb implements Iterable<BreadCrumbElement> { private static final BreadCrumb EMPTY = new BreadCrumb(); private final List<BreadCrumbElement> elements = new LinkedList<BreadCrumbElement>(); private String bc; BreadCrumb(final String bc) throws BreadCrumbException { final Set<BreadCrumbElement> set = new HashSet<BreadCrumbElement>(); BreadCrumbElement e; for (final String element: bc.split("\\s+")) { e = new BreadCrumbElement(element); if (!set.add(e)) throw new BreadCrumbException("Élément dupliqué " + "dans le fil d'Ariane : " + element); elements.add(e); } if (elements.isEmpty()) throw new BreadCrumbException("Fil d'ariane vide!"); if (!elements.get(0).visibleFrom(Visibility.MAINPAGE)) throw new BreadCrumbException("Le fil d'Ariane ne " + "commence pas à l'accueil : " + bc); set.clear(); this.bc = bc; } private BreadCrumb() { } BreadCrumb reverse() { final BreadCrumb ret = new BreadCrumb(); ret.elements.addAll(elements); Collections.reverse(ret.elements); ret.bc = StringUtils.join(ret.elements, " "); return ret; } public Iterator<BreadCrumbElement> iterator() { return elements.iterator(); } @Override public String toString() { return bc; } }
Interface for palette renderer:
public interface BreadCrumbRender { List<CTObjectBean> getBreadCrumb() throws Throwable; String getTopCategory(); String getMenuRoot(); String getContext(); }
The implementation of the interface above which is the source of my problems:
private class CategoryBreadCrumbRender implements BreadCrumbRender { private final BreadCrumb bc; private final CTObject object; CategoryBreadCrumbRender(final CTObject object) { this.object = object; final String property;
At the point marked by // <-- HERE above, the Intellij IDEA I use and javac (1.6.0.29) will tell me that Variable bc might already have been assigned to , which is considered an error (and indeed, the code does not compile )
The problem is that I do not understand why ... My reasoning is this:
- in the first try / catch block (and yes,
.getProperty() makes a throw Throwable) when the exception is caught, bc gets assigned for success, and then I return how good it is; - in the second try / catch block, the constructor may fail, and in this case I will assign an empty palette, so it should be OK, although
bc is final: the assignment is not executed (?) in try to execute the block, but it will happen in the catch block. ..
Except no, it is not. Since both IDEA and javac disagree with me, they are certainly right. But why?
(and also BreadCrumb.EMPTY declared private static final in the class, I wonder how I can access it at all ... Helper question)
EDIT : there is a known error with the final keyword ( here , thanks to @MiladNaseri for linking to it), however, it should be noted that in this error the variable v is assigned only in catch blocks, but in the above code I assign it in try blocks and assign it only blocks in catch if an exception is thrown. In addition, it should be noted that an error occurs only in the second catch .