m = cache.get(method);
if (m == null) return null;
int n = m.size();
for (int i = 0; i < n; i++) {
Method me = m.get(i);
if (isStaticMethod(me) && call_checkArgs(me, args, false))
return me;
}
return null;
} catch (Exception __e) { throw rethrow(__e); } }
}
static abstract class VF1 implements IVF1 {
public abstract void get(A a);
}
// Meta - a "minimal" approach to adding meta-level to Java objects
static class Meta implements IMeta {
// We allocate one extra field for each Java object to make it
// reasoning-compatible (reasoning-compatible = extensible with
// fields of any name at runtime).
//
// We couldn't go for 0 extra fields (meta values must be linked
// directly from the object) and there are no half fields in
// Java... so there you go.
//
// Also, if you don't use any meta data, you are probably not
// reasoning about anything. The point of reasoning in JavaX is
// to attach information to objects directly used in the program.
// Possible information contained in the meta field:
// Origin, destination, security level, sender, cost center,
// purpose, list of reifications, ...
// So here it is. THE FIELD YOU HAVE BEEN WAITING FOR!
// [We also have IMeta to retrofit foreign classes (rare but
// probably useful).]
//////////////////////
// The "meta" field //
//////////////////////
// Generic meta value of any kind, but the typical case is it's a
// Map with extra field values for the object etc.
// "meta" is volatile to avoid synchronization; but you can also synchronize on
// _tempMetaMutex() which is usually the object itself. Collections
// and maps are exempt from using the collections's monitor as the meta
// mutex because their monitor tends to be held for long operations
// (e.g. cloneList). For those we use a substantially more complex
// algorithm using a weakMap. Probably overkill. I may reconsider.
volatile Object meta;
// The meta field is not transient, thus by default it will be
// persisted like anything else unless you customize your object
// to suppress or modulate this.
// ...and the interface methods
public void _setMeta(Object meta) { this.meta = meta; }
public Object _getMeta() { return meta; }
// MOST functions are implemented in IMeta (default implementations)
}
/**
* A class that provides scrolling capabilities to a long menu dropdown or
* popup menu. A number of items can optionally be frozen at the top and/or
* bottom of the menu.
*
* Implementation note: The default number of items to display
* at a time is 15, and the default scrolling interval is 125 milliseconds.
*
*
* @version 1.5.0 04/05/12
* @author Darryl
* https://tips4java.wordpress.com/2009/02/01/menu-scroller/
*/
static class JMenuScroller {
VF1 fillMenu;
private JPopupMenu menu;
private Component[] menuItems;
private MenuScrollItem upItem;
private MenuScrollItem downItem;
private final MenuScrollListener menuListener = new MenuScrollListener();
private int scrollCount;
private int interval;
private int topFixedCount;
private int bottomFixedCount;
private int firstIndex = 0;
private int keepVisibleIndex = -1;
/**
* Registers a menu to be scrolled with the default number of items to
* display at a time and the default scrolling interval.
*
* @param menu the menu
* @return the JMenuScroller
*/
public static JMenuScroller setScrollerFor(JMenu menu) {
return new JMenuScroller(menu);
}
/**
* Registers a popup menu to be scrolled with the default number of items to
* display at a time and the default scrolling interval.
*
* @param menu the popup menu
* @return the JMenuScroller
*/
public static JMenuScroller setScrollerFor(JPopupMenu menu) {
return new JMenuScroller(menu);
}
/**
* Registers a menu to be scrolled with the default number of items to
* display at a time and the specified scrolling interval.
*
* @param menu the menu
* @param scrollCount the number of items to display at a time
* @return the JMenuScroller
* @throws IllegalArgumentException if scrollCount is 0 or negative
*/
public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount) {
return new JMenuScroller(menu, scrollCount);
}
/**
* Registers a popup menu to be scrolled with the default number of items to
* display at a time and the specified scrolling interval.
*
* @param menu the popup menu
* @param scrollCount the number of items to display at a time
* @return the JMenuScroller
* @throws IllegalArgumentException if scrollCount is 0 or negative
*/
public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount) {
return new JMenuScroller(menu, scrollCount);
}
/**
* Registers a menu to be scrolled, with the specified number of items to
* display at a time and the specified scrolling interval.
*
* @param menu the menu
* @param scrollCount the number of items to be displayed at a time
* @param interval the scroll interval, in milliseconds
* @return the JMenuScroller
* @throws IllegalArgumentException if scrollCount or interval is 0 or negative
*/
public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval) {
return new JMenuScroller(menu, scrollCount, interval);
}
/**
* Registers a popup menu to be scrolled, with the specified number of items to
* display at a time and the specified scrolling interval.
*
* @param menu the popup menu
* @param scrollCount the number of items to be displayed at a time
* @param interval the scroll interval, in milliseconds
* @return the JMenuScroller
* @throws IllegalArgumentException if scrollCount or interval is 0 or negative
*/
public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval) {
return new JMenuScroller(menu, scrollCount, interval);
}
/**
* Registers a menu to be scrolled, with the specified number of items
* to display in the scrolling region, the specified scrolling interval,
* and the specified numbers of items fixed at the top and bottom of the
* menu.
*
* @param menu the menu
* @param scrollCount the number of items to display in the scrolling portion
* @param interval the scroll interval, in milliseconds
* @param topFixedCount the number of items to fix at the top. May be 0.
* @param bottomFixedCount the number of items to fix at the bottom. May be 0
* @throws IllegalArgumentException if scrollCount or interval is 0 or
* negative or if topFixedCount or bottomFixedCount is negative
* @return the JMenuScroller
*/
public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval,
int topFixedCount, int bottomFixedCount) {
return new JMenuScroller(menu, scrollCount, interval,
topFixedCount, bottomFixedCount);
}
/**
* Registers a popup menu to be scrolled, with the specified number of items
* to display in the scrolling region, the specified scrolling interval,
* and the specified numbers of items fixed at the top and bottom of the
* popup menu.
*
* @param menu the popup menu
* @param scrollCount the number of items to display in the scrolling portion
* @param interval the scroll interval, in milliseconds
* @param topFixedCount the number of items to fix at the top. May be 0
* @param bottomFixedCount the number of items to fix at the bottom. May be 0
* @throws IllegalArgumentException if scrollCount or interval is 0 or
* negative or if topFixedCount or bottomFixedCount is negative
* @return the JMenuScroller
*/
public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval,
int topFixedCount, int bottomFixedCount) {
return new JMenuScroller(menu, scrollCount, interval,
topFixedCount, bottomFixedCount);
}
/**
* Constructs a JMenuScroller that scrolls a menu with the
* default number of items to display at a time, and default scrolling
* interval.
*
* @param menu the menu
*/
public JMenuScroller(JMenu menu) {
this(menu, 15);
}
/**
* Constructs a JMenuScroller that scrolls a popup menu with the
* default number of items to display at a time, and default scrolling
* interval.
*
* @param menu the popup menu
*/
public JMenuScroller(JPopupMenu menu) {
this(menu, 15);
}
/**
* Constructs a JMenuScroller that scrolls a menu with the
* specified number of items to display at a time, and default scrolling
* interval.
*
* @param menu the menu
* @param scrollCount the number of items to display at a time
* @throws IllegalArgumentException if scrollCount is 0 or negative
*/
public JMenuScroller(JMenu menu, int scrollCount) {
this(menu, scrollCount, 150);
}
/**
* Constructs a JMenuScroller that scrolls a popup menu with the
* specified number of items to display at a time, and default scrolling
* interval.
*
* @param menu the popup menu
* @param scrollCount the number of items to display at a time
* @throws IllegalArgumentException if scrollCount is 0 or negative
*/
public JMenuScroller(JPopupMenu menu, int scrollCount) {
this(menu, scrollCount, 150);
}
/**
* Constructs a JMenuScroller that scrolls a menu with the
* specified number of items to display at a time, and specified scrolling
* interval.
*
* @param menu the menu
* @param scrollCount the number of items to display at a time
* @param interval the scroll interval, in milliseconds
* @throws IllegalArgumentException if scrollCount or interval is 0 or negative
*/
public JMenuScroller(JMenu menu, int scrollCount, int interval) {
this(menu, scrollCount, interval, 0, 0);
}
/**
* Constructs a JMenuScroller that scrolls a popup menu with the
* specified number of items to display at a time, and specified scrolling
* interval.
*
* @param menu the popup menu
* @param scrollCount the number of items to display at a time
* @param interval the scroll interval, in milliseconds
* @throws IllegalArgumentException if scrollCount or interval is 0 or negative
*/
public JMenuScroller(JPopupMenu menu, int scrollCount, int interval) {
this(menu, scrollCount, interval, 0, 0);
}
/**
* Constructs a JMenuScroller that scrolls a menu with the
* specified number of items to display in the scrolling region, the
* specified scrolling interval, and the specified numbers of items fixed at
* the top and bottom of the menu.
*
* @param menu the menu
* @param scrollCount the number of items to display in the scrolling portion
* @param interval the scroll interval, in milliseconds
* @param topFixedCount the number of items to fix at the top. May be 0
* @param bottomFixedCount the number of items to fix at the bottom. May be 0
* @throws IllegalArgumentException if scrollCount or interval is 0 or
* negative or if topFixedCount or bottomFixedCount is negative
*/
public JMenuScroller(JMenu menu, int scrollCount, int interval,
int topFixedCount, int bottomFixedCount) {
this(menu.getPopupMenu(), scrollCount, interval, topFixedCount, bottomFixedCount);
}
/**
* Constructs a JMenuScroller that scrolls a popup menu with the
* specified number of items to display in the scrolling region, the
* specified scrolling interval, and the specified numbers of items fixed at
* the top and bottom of the popup menu.
*
* @param menu the popup menu
* @param scrollCount the number of items to display in the scrolling portion
* @param interval the scroll interval, in milliseconds
* @param topFixedCount the number of items to fix at the top. May be 0
* @param bottomFixedCount the number of items to fix at the bottom. May be 0
* @throws IllegalArgumentException if scrollCount or interval is 0 or
* negative or if topFixedCount or bottomFixedCount is negative
*/
public JMenuScroller(JPopupMenu menu, int scrollCount, int interval,
int topFixedCount, int bottomFixedCount) {
if (scrollCount <= 0 || interval <= 0) {
throw new IllegalArgumentException("scrollCount and interval must be greater than 0");
}
if (topFixedCount < 0 || bottomFixedCount < 0) {
throw new IllegalArgumentException("topFixedCount and bottomFixedCount cannot be negative");
}
upItem = new MenuScrollItem(UP, -1);
downItem = new MenuScrollItem(DOWN, +1);
setScrollCount(scrollCount);
setInterval(interval);
setTopFixedCount(topFixedCount);
setBottomFixedCount(bottomFixedCount);
this.menu = menu;
menu.addPopupMenuListener(menuListener);
}
/**
* Returns the scroll interval in milliseconds
*
* @return the scroll interval in milliseconds
*/
public int getInterval() {
return interval;
}
/**
* Sets the scroll interval in milliseconds
*
* @param interval the scroll interval in milliseconds
* @throws IllegalArgumentException if interval is 0 or negative
*/
public void setInterval(int interval) {
if (interval <= 0) {
throw new IllegalArgumentException("interval must be greater than 0");
}
upItem.setInterval(interval);
downItem.setInterval(interval);
this.interval = interval;
}
/**
* Returns the number of items in the scrolling portion of the menu.
*
* @return the number of items to display at a time
*/
public int getscrollCount() {
return scrollCount;
}
/**
* Sets the number of items in the scrolling portion of the menu.
*
* @param scrollCount the number of items to display at a time
* @throws IllegalArgumentException if scrollCount is 0 or negative
*/
public void setScrollCount(int scrollCount) {
if (scrollCount <= 0) {
throw new IllegalArgumentException("scrollCount must be greater than 0");
}
this.scrollCount = scrollCount;
// XXX the following line closes all menus then this menu is made.
// That doesn't seem right.
// MenuSelectionManager.defaultManager().clearSelectedPath();
}
/**
* Returns the number of items fixed at the top of the menu or popup menu.
*
* @return the number of items
*/
public int getTopFixedCount() {
return topFixedCount;
}
/**
* Sets the number of items to fix at the top of the menu or popup menu.
*
* @param topFixedCount the number of items
*/
public void setTopFixedCount(int topFixedCount) {
if (firstIndex <= topFixedCount) {
firstIndex = topFixedCount;
} else {
firstIndex += (topFixedCount - this.topFixedCount);
}
this.topFixedCount = topFixedCount;
}
/**
* Returns the number of items fixed at the bottom of the menu or popup menu.
*
* @return the number of items
*/
public int getBottomFixedCount() {
return bottomFixedCount;
}
/**
* Sets the number of items to fix at the bottom of the menu or popup menu.
*
* @param bottomFixedCount the number of items
*/
public void setBottomFixedCount(int bottomFixedCount) {
this.bottomFixedCount = bottomFixedCount;
}
/**
* Scrolls the specified item into view each time the menu is opened. Call this method with
* null to restore the default behavior, which is to show the menu as it last
* appeared.
*
* @param item the item to keep visible
* @see #keepVisible(int)
*/
public void keepVisible(JMenuItem item) {
if (item == null) {
keepVisibleIndex = -1;
} else {
int index = menu.getComponentIndex(item);
keepVisibleIndex = index;
}
}
/**
* Scrolls the item at the specified index into view each time the menu is opened. Call this
* method with -1 to restore the default behavior, which is to show the menu as
* it last appeared.
*
* @param index the index of the item to keep visible
* @see #keepVisible(javax.swing.JMenuItem)
*/
public void keepVisible(int index) {
keepVisibleIndex = index;
}
/**
* Removes this JMenuScroller from the associated menu and restores the
* default behavior of the menu.
*/
public void dispose() {
if (menu != null) {
menu.removePopupMenuListener(menuListener);
menu = null;
}
}
/**
* Ensures that the dispose method of this JMenuScroller is
* called when there are no more refrences to it.
*
* @exception Throwable if an error occurs.
* @see JMenuScroller#dispose()
*/
@Override
public void finalize() throws Throwable {
dispose();
}
private void refreshMenu() {
if (menuItems != null && menuItems.length > 0) {
firstIndex = Math.max(topFixedCount, firstIndex);
firstIndex = Math.min(menuItems.length - bottomFixedCount - scrollCount, firstIndex);
upItem.setEnabled(firstIndex > topFixedCount);
downItem.setEnabled(firstIndex + scrollCount < menuItems.length - bottomFixedCount);
menu.removeAll();
for (int i = 0; i < topFixedCount; i++) {
menu.add(menuItems[i]);
}
if (topFixedCount > 0) {
menu.addSeparator();
}
menu.add(upItem);
for (int i = firstIndex; i < scrollCount + firstIndex; i++) {
menu.add(menuItems[i]);
}
menu.add(downItem);
if (bottomFixedCount > 0) {
menu.addSeparator();
}
for (int i = menuItems.length - bottomFixedCount; i < menuItems.length; i++) {
menu.add(menuItems[i]);
}
JComponent parent = (JComponent) upItem.getParent();
parent.revalidate();
parent.repaint();
}
}
private class MenuScrollListener implements PopupMenuListener {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
if (fillMenu != null) {
clearPopupMenu(menu);
callF(fillMenu, menu);
}
setMenuItems();
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
if (fillMenu != null) clearPopupMenu(menu);
else restoreMenuItems();
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
if (fillMenu != null) clearPopupMenu(menu);
else restoreMenuItems();
}
private void setMenuItems() {
menuItems = menu.getComponents();
if (keepVisibleIndex >= topFixedCount
&& keepVisibleIndex <= menuItems.length - bottomFixedCount
&& (keepVisibleIndex > firstIndex + scrollCount
|| keepVisibleIndex < firstIndex)) {
firstIndex = Math.min(firstIndex, keepVisibleIndex);
firstIndex = Math.max(firstIndex, keepVisibleIndex - scrollCount + 1);
}
if (menuItems.length > topFixedCount + scrollCount + bottomFixedCount) {
refreshMenu();
}
}
private void restoreMenuItems() {
menu.removeAll();
for (Component component : menuItems) {
menu.add(component);
}
}
}
private class MenuScrollTimer extends javax.swing.Timer {
public MenuScrollTimer(final int increment, int interval) {
super(interval, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
firstIndex += increment;
refreshMenu();
}
});
}
}
private class MenuScrollItem extends JMenuItem
implements ChangeListener {
private MenuScrollTimer timer;
public MenuScrollItem(MenuIcon icon, int increment) {
setIcon(icon);
setDisabledIcon(icon);
timer = new MenuScrollTimer(increment, interval);
addChangeListener(this);
}
public void setInterval(int interval) {
timer.setDelay(interval);
}
@Override
public void stateChanged(ChangeEvent e) {
if (isArmed() && !timer.isRunning()) {
timer.start();
}
if (!isArmed() && timer.isRunning()) {
timer.stop();
}
}
}
static MenuIcon UP = new MenuIcon(9, 1, 9);
static MenuIcon DOWN = new MenuIcon(1, 9, 1);
private static class MenuIcon implements Icon {
final int[] xPoints = {1, 5, 9};
final int[] yPoints;
MenuIcon(int... yPoints) {
this.yPoints = yPoints;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
Dimension size = c.getSize();
Graphics g2 = g.create(size.width / 2 - 5, size.height / 2 - 5, 10, 10);
g2.setColor(Color.GRAY);
g2.drawPolygon(xPoints, yPoints, 3);
if (c.isEnabled()) {
g2.setColor(Color.BLACK);
g2.fillPolygon(xPoints, yPoints, 3);
}
g2.dispose();
}
@Override
public int getIconWidth() {
return 0;
}
@Override
public int getIconHeight() {
return 10;
}
}
}
static class Matches {
String[] m;
Matches() {}
Matches(String... m) {
this.m = m;}
String get(int i) { return i < m.length ? m[i] : null; }
String unq(int i) { return unquote(get(i)); }
String tlc(int i) { return unq(i).toLowerCase(); }
boolean bool(int i) { return "true".equals(unq(i)); }
String rest() { return m[m.length-1]; } // for matchStart
int psi(int i) { return Integer.parseInt(unq(i)); }
public String toString() { return "Matches(" + joinWithComma(quoteAll(asList(m))) + ")"; }
public int hashCode() { return _hashCode(toList(m)); }
public boolean equals(Object o) { return o instanceof Matches && arraysEqual(m, ((Matches) o).m); }
}
// for the version with MasterSymbol (used WAY back in "Smart Bot"!) see #1010608
static class Symbol implements CharSequence {
String text;
Symbol() {}
Symbol(String text, boolean dummy) {
this.text = text;} // weird signature to prevent accidental calling
public int hashCode() { return _hashCode(text); }
public String toString() { return text; }
public boolean equals(Object o) {
return this == o;
}
// implementation of CharSequence methods
public int length() { return text.length(); }
public char charAt(int index) { return text.charAt(index); }
public CharSequence subSequence(int start, int end) {
return text.substring(start, end);
}
}
static class Var implements IVar, ISetter {
Var() {}
Var(A v) {
this.v = v;}
A v; // you can access this directly if you use one thread
public synchronized void set(A a) {
if (v != a) {
v = a;
notifyAll();
}
}
public synchronized A get() { return v; }
public synchronized boolean has() { return v != null; }
public void clear() { set(null); }
public String toString() { return str(this.get()); }
}
static interface IMeta {
// see class "Meta" for the bla bla
public void _setMeta(Object meta);
public Object _getMeta();
default public IAutoCloseableF0 _tempMetaMutex() {
return new IAutoCloseableF0() {
public Object get() { return IMeta.this; }
public void close() {}
};
}
// actually query another object
default public Object getMeta(Object obj, Object key){ return metaGet(obj, key); }
default public Object metaGet(Object obj, Object key) {
// call global function
return metaMapGet(obj, key);
}
default public Object metaGet(String key, Object obj) {
// call global function
return metaMapGet(obj, key);
}
default public Object getMeta(Object key){ return metaGet(key); }
default public Object metaGet(Object key) {
if (key == null) return null;
Object meta = _getMeta();
if (meta instanceof Map) return ((Map) meta).get(key);
return null;
}
default public void metaSet(IMeta obj, Object key, Object value){ metaPut(obj, key, value); }
default public void metaPut(IMeta obj, Object key, Object value) {
// call global function
metaMapPut(obj, key, value);
}
default public void metaSet(Object key, Object value){ metaPut(key, value); }
default public void metaPut(Object key, Object value) {
if (key == null) return;
Map map = convertObjectMetaToMap(this);
syncMapPutOrRemove(map, key, value);
}
}
// a variant of thread where you can get the Runnable target later.
// Also notes its existence on the VM bus.
// We should use this exclusively instead of Thread.
static class BetterThread extends Thread {
Runnable target;
BetterThread(Runnable target) {
this.target = target; _created(); }
BetterThread(Runnable target, String name) { super(name);
this.target = target; _created(); }
void _created() { vmBus_send("threadCreated", this); }
public void run() { try {
try {
vmBus_send("threadStarted", this);
if (target != null) target.run();
} finally {
vmBus_send("threadEnded", this);
}
} catch (Exception __e) { throw rethrow(__e); } }
Runnable getTarget() { return target; }
}
static abstract class F0 {
abstract A get();
}
static abstract class F1 {
abstract B get(A a);
}
// you still need to implement hasNext() and next()
static abstract class IterableIterator implements Iterator, Iterable {
public Iterator iterator() {
return this;
}
public void remove() {
unsupportedOperation();
}
}
static class MultiMap {
Map> data = new HashMap>();
int fullSize;
MultiMap() {}
MultiMap(boolean useTreeMap) { if (useTreeMap) data = new TreeMap(); }
MultiMap(MultiMap map) { putAll(map); }
MultiMap(Map> data) {
this.data = data;}
void put(A key, B value) { synchronized(data) {
List list = data.get(key);
if (list == null)
data.put(key, list = _makeEmptyList());
list.add(value);
++fullSize;
}}
void add(A key, B value) { put(key, value); }
void addAll(A key, Collection values) { putAll(key, values); }
void addAllIfNotThere(A key, Collection values) { synchronized(data) {
for (B value : values)
setPut(key, value);
}}
void setPut(A key, B value) { synchronized(data) {
if (!containsPair(key, value))
put(key, value);
}}
boolean containsPair(A key, B value) { synchronized(data) {
return get(key).contains(value);
}}
void putAll(Collection keys, B value) { synchronized(data) {
for (A key : unnullForIteration(keys))
put(key, value);
}}
void putAll(A key, Collection values) { synchronized(data) {
if (nempty(values)) getActual(key).addAll(values);
}}
void putAll(Iterable> pairs) { synchronized(data) {
for (Pair p : unnullForIteration(pairs))
put(p.a, p.b);
}}
void removeAll(A key, Collection values) { synchronized(data) {
for (B value : values)
remove(key, value);
}}
List get(A key) { synchronized(data) {
List list = data.get(key);
return list == null ? Collections. emptyList() : list;
}}
List getOpt(A key) { synchronized(data) {
return data.get(key);
}}
List getAndClear(A key) { synchronized(data) {
List l = cloneList(data.get(key));
remove(key);
return l;
}}
// returns actual mutable live list
// creates the list if not there
List getActual(A key) { synchronized(data) {
List list = data.get(key);
if (list == null)
data.put(key, list = _makeEmptyList());
return list;
}}
void clean(A key) { synchronized(data) {
List list = data.get(key);
if (list != null && list.isEmpty()) {
fullSize -= l(list);
data.remove(key);
}
}}
Set keySet() { synchronized(data) {
return data.keySet();
}}
Set keys() { synchronized(data) {
return data.keySet();
}}
void remove(A key) { synchronized(data) {
fullSize -= l(this.getOpt(key));
data.remove(key);
}}
final void remove(Pair p){ removePair(p); }
void removePair(Pair p) {
if (p != null) remove(p.a, p.b);
}
void remove(A key, B value) { synchronized(data) {
List list = data.get(key);
if (list != null) {
if (list.remove(value))
fullSize--;
if (list.isEmpty())
data.remove(key);
}
}}
void clear() { synchronized(data) {
data.clear();
}}
boolean containsKey(A key) { synchronized(data) {
return data.containsKey(key);
}}
B getFirst(A key) { synchronized(data) {
List list = get(key);
return list.isEmpty() ? null : list.get(0);
}}
void addAll(MultiMap map) { putAll(map); }
void putAll(MultiMap map) { synchronized(data) {
for (A key : map.keySet())
putAll(key, map.get(key));
}}
void putAll(Map map) { synchronized(data) {
if (map != null) for (Map.Entry e : map.entrySet())
put(e.getKey(), e.getValue());
}}
final int keyCount(){ return keysSize(); }
int keysSize() { synchronized(data) { return l(data); }}
// full size - note: expensive operation
final int fullSize(){ return size(); }
int size() { synchronized(data) {
return fullSize;
}}
// expensive operation
List reverseGet(B b) { synchronized(data) {
List l = new ArrayList();
for (A key : data.keySet())
if (data.get(key).contains(b))
l.add(key);
return l;
}}
Map> asMap() { synchronized(data) {
return cloneMap(data);
}}
boolean isEmpty() { synchronized(data) { return data.isEmpty(); }}
// override in subclasses
List _makeEmptyList() {
return new ArrayList();
}
// returns live lists
Collection> allLists() {
synchronized(data) {
return new ArrayList(data.values());
}
}
Collection> values() { return allLists(); }
List allValues() {
return concatLists(data.values());
}
Object mutex() { return data; }
public String toString() { return "mm" + str(data); }
}
static interface IF0 {
A get();
}
static interface Hasher {
int hashCode(A a);
boolean equals(A a, A b);
}
static interface IF1 {
B get(A a);
}
/*
* @(#)WeakHashMap.java 1.5 98/09/30
*
* Copyright 1998 by Sun Microsystems, Inc.,
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of Sun Microsystems, Inc. ("Confidential Information"). You
* shall not disclose such Confidential Information and shall use
* it only in accordance with the terms of the license agreement
* you entered into with Sun.
*/
// From https://github.com/mernst/plume-lib/blob/df0bfafc3c16848d88f4ea0ef3c8bf3367ae085e/java/src/plume/WeakHasherMap.java
static final class WeakHasherMap extends AbstractMap implements Map {
private Hasher hasher = null;
/*@Pure*/
private boolean keyEquals(Object k1, Object k2) {
return (hasher==null ? k1.equals(k2)
: hasher.equals(k1, k2));
}
/*@Pure*/
private int keyHashCode(Object k1) {
return (hasher==null ? k1.hashCode()
: hasher.hashCode(k1));
}
// The WeakKey class can't be static because it depends on the hasher.
// That in turn means that its methods can't be static.
// However, I need to be able to call the methods such as create() that
// were static in the original version of this code.
// This finesses that.
private /*@Nullable*/ WeakKey WeakKeyCreate(K k) {
if (k == null) return null;
else return new WeakKey(k);
}
private /*@Nullable*/ WeakKey WeakKeyCreate(K k, ReferenceQueue super K> q) {
if (k == null) return null;
else return new WeakKey(k, q);
}
// Cannot be a static class: uses keyHashCode() and keyEquals()
private final class WeakKey extends WeakReference {
private int hash; /* Hashcode of key, stored here since the key
may be tossed by the GC */
private WeakKey(K k) {
super(k);
hash = keyHashCode(k);
}
private /*@Nullable*/ WeakKey create(K k) {
if (k == null) return null;
else return new WeakKey(k);
}
private WeakKey(K k, ReferenceQueue super K> q) {
super(k, q);
hash = keyHashCode(k);
}
private /*@Nullable*/ WeakKey create(K k, ReferenceQueue super K> q) {
if (k == null) return null;
else return new WeakKey(k, q);
}
/* A WeakKey is equal to another WeakKey iff they both refer to objects
that are, in turn, equal according to their own equals methods */
/*@Pure*/
@Override
public boolean equals(/*@Nullable*/ Object o) {
if (o == null) return false; // never happens
if (this == o) return true;
// This test is illegal because WeakKey is a generic type,
// so use the getClass hack below instead.
// if (!(o instanceof WeakKey)) return false;
if (!(o.getClass().equals(WeakKey.class))) return false;
Object t = this.get();
@SuppressWarnings("unchecked")
Object u = ((WeakKey)o).get();
if ((t == null) || (u == null)) return false;
if (t == u) return true;
return keyEquals(t, u);
}
/*@Pure*/
@Override
public int hashCode() {
return hash;
}
}
/* Hash table mapping WeakKeys to values */
private HashMap hash;
/* Reference queue for cleared WeakKeys */
private ReferenceQueue super K> queue = new ReferenceQueue();
/* Remove all invalidated entries from the map, that is, remove all entries
whose keys have been discarded. This method should be invoked once by
each public mutator in this class. We don't invoke this method in
public accessors because that can lead to surprising
ConcurrentModificationExceptions. */
@SuppressWarnings("unchecked")
private void processQueue() {
WeakKey wk;
while ((wk = (WeakKey)queue.poll()) != null) { // unchecked cast
hash.remove(wk);
}
}
/* -- Constructors -- */
/**
* Constructs a new, empty WeakHashMap with the given
* initial capacity and the given load factor.
*
* @param initialCapacity the initial capacity of the
* WeakHashMap
*
* @param loadFactor the load factor of the WeakHashMap
*
* @throws IllegalArgumentException If the initial capacity is less than
* zero, or if the load factor is
* nonpositive
*/
public WeakHasherMap(int initialCapacity, float loadFactor) {
hash = new HashMap(initialCapacity, loadFactor);
}
/**
* Constructs a new, empty WeakHashMap with the given
* initial capacity and the default load factor, which is
* 0.75.
*
* @param initialCapacity the initial capacity of the
* WeakHashMap
*
* @throws IllegalArgumentException If the initial capacity is less than
* zero
*/
public WeakHasherMap(int initialCapacity) {
hash = new HashMap(initialCapacity);
}
/**
* Constructs a new, empty WeakHashMap with the default
* capacity and the default load factor, which is 0.75.
*/
public WeakHasherMap() {
hash = new HashMap();
}
/**
* Constructs a new, empty WeakHashMap with the default
* capacity and the default load factor, which is 0.75.
* The WeakHashMap uses the specified hasher for hashing
* keys and comparing them for equality.
* @param h the Hasher to use when hashing values for this map
*/
public WeakHasherMap(Hasher h) {
hash = new HashMap();
hasher = h;
}
/* -- Simple queries -- */
/**
* Returns the number of key-value mappings in this map.
* Note: In contrast to most implementations of the
* Map interface, the time required by this operation is
* linear in the size of the map.
*/
/*@Pure*/
@Override
public int size() {
return entrySet().size();
}
/**
* Returns true if this map contains no key-value mappings.
*/
/*@Pure*/
@Override
public boolean isEmpty() {
return entrySet().isEmpty();
}
/**
* Returns true if this map contains a mapping for the
* specified key.
*
* @param key the key whose presence in this map is to be tested
*/
/*@Pure*/
@Override
public boolean containsKey(Object key) {
@SuppressWarnings("unchecked")
K kkey = (K) key;
return hash.containsKey(WeakKeyCreate(kkey));
}
/* -- Lookup and modification operations -- */
/**
* Returns the value to which this map maps the specified key.
* If this map does not contain a value for this key, then return
* null.
*
* @param key the key whose associated value, if any, is to be returned
*/
/*@Pure*/
@Override
public /*@Nullable*/ V get(Object key) { // type of argument is Object, not K
@SuppressWarnings("unchecked")
K kkey = (K) key;
return hash.get(WeakKeyCreate(kkey));
}
/**
* Updates this map so that the given key maps to the given
* value. If the map previously contained a mapping for
* key then that mapping is replaced and the previous value is
* returned.
*
* @param key the key that is to be mapped to the given
* value
* @param value the value to which the given key is to be
* mapped
*
* @return the previous value to which this key was mapped, or
* null if if there was no mapping for the key
*/
@Override
public V put(K key, V value) {
processQueue();
return hash.put(WeakKeyCreate(key, queue), value);
}
/**
* Removes the mapping for the given key from this map, if
* present.
*
* @param key the key whose mapping is to be removed
*
* @return the value to which this key was mapped, or null if
* there was no mapping for the key
*/
@Override
public V remove(Object key) { // type of argument is Object, not K
processQueue();
@SuppressWarnings("unchecked")
K kkey = (K) key;
return hash.remove(WeakKeyCreate(kkey));
}
/**
* Removes all mappings from this map.
*/
@Override
public void clear() {
processQueue();
hash.clear();
}
/* -- Views -- */
/* Internal class for entries */
// This can't be static, again because of dependence on hasher.
@SuppressWarnings("TypeParameterShadowing")
private final class Entry implements Map.Entry {
private Map.Entry ent;
private K key; /* Strong reference to key, so that the GC
will leave it alone as long as this Entry
exists */
Entry(Map.Entry ent, K key) {
this.ent = ent;
this.key = key;
}
/*@Pure*/
@Override
public K getKey() {
return key;
}
/*@Pure*/
@Override
public V getValue() {
return ent.getValue();
}
@Override
public V setValue(V value) {
return ent.setValue(value);
}
/*@Pure*/
private boolean keyvalEquals(K o1, K o2) {
return (o1 == null) ? (o2 == null) : keyEquals(o1, o2);
}
/*@Pure*/
private boolean valEquals(V o1, V o2) {
return (o1 == null) ? (o2 == null) : o1.equals(o2);
}
/*@Pure*/
@SuppressWarnings("NonOverridingEquals")
public boolean equals(Map.Entry e /* Object o*/) {
// if (! (o instanceof Map.Entry)) return false;
// Map.Entry e = (Map.Entry)o;
return (keyvalEquals(key, e.getKey())
&& valEquals(getValue(), e.getValue()));
}
/*@Pure*/
@Override
public int hashCode() {
V v;
return (((key == null) ? 0 : keyHashCode(key))
^ (((v = getValue()) == null) ? 0 : v.hashCode()));
}
}
/* Internal class for entry sets */
private final class EntrySet extends AbstractSet> {
Set> hashEntrySet = hash.entrySet();
@Override
public Iterator> iterator() {
return new Iterator>() {
Iterator> hashIterator = hashEntrySet.iterator();
Map.Entry next = null;
@Override
public boolean hasNext() {
while (hashIterator.hasNext()) {
Map.Entry ent = hashIterator.next();
WeakKey wk = ent.getKey();
K k = null;
if ((wk != null) && ((k = wk.get()) == null)) {
/* Weak key has been cleared by GC */
continue;
}
next = new Entry(ent, k);
return true;
}
return false;
}
@Override
public Map.Entry next() {
if ((next == null) && !hasNext())
throw new NoSuchElementException();
Map.Entry e = next;
next = null;
return e;
}
@Override
public void remove() {
hashIterator.remove();
}
};
}
/*@Pure*/
@Override
public boolean isEmpty() {
return !(iterator().hasNext());
}
/*@Pure*/
@Override
public int size() {
int j = 0;
for (Iterator> i = iterator(); i.hasNext(); i.next()) j++;
return j;
}
@Override
public boolean remove(Object o) {
processQueue();
if (!(o instanceof Map.Entry,?>)) return false;
@SuppressWarnings("unchecked")
Map.Entry e = (Map.Entry)o; // unchecked cast
Object ev = e.getValue();
WeakKey wk = WeakKeyCreate(e.getKey());
Object hv = hash.get(wk);
if ((hv == null)
? ((ev == null) && hash.containsKey(wk)) : hv.equals(ev)) {
hash.remove(wk);
return true;
}
return false;
}
/*@Pure*/
@Override
public int hashCode() {
int h = 0;
for (Iterator> i = hashEntrySet.iterator(); i.hasNext(); ) {
Map.Entry ent = i.next();
WeakKey wk = ent.getKey();
Object v;
if (wk == null) continue;
h += (wk.hashCode()
^ (((v = ent.getValue()) == null) ? 0 : v.hashCode()));
}
return h;
}
}
private /*@Nullable*/ Set> entrySet = null;
/**
* Returns a Set view of the mappings in this map.
*/
/*@SideEffectFree*/
@Override
public Set> entrySet() {
if (entrySet == null) entrySet = new EntrySet();
return entrySet;
}
// find matching key
K findKey(Object key) {
processQueue();
K kkey = (K) key;
// TODO: use replacement for HashMap to avoid reflection
WeakKey wkey = WeakKeyCreate(kkey);
WeakKey found = hashMap_findKey(hash, wkey);
return found == null ? null : found.get();
}
}
static class PersistableThrowable extends DynamicObject {
String className;
String msg;
String stacktrace;
PersistableThrowable() {}
PersistableThrowable(Throwable e) {
if (e == null)
className = "Crazy Null Error";
else {
className = getClassName(e).replace('/', '.');
msg = e.getMessage();
stacktrace = getStackTrace_noRecord(e);
}
}
public String toString() {
return nempty(msg) ? className + ": " + msg : className;
}
}
static interface IVF1 {
void get(A a);
}
static class Fail extends RuntimeException implements IFieldsToList{
Object[] objects;
Fail() {}
Fail(Object... objects) {
this.objects = objects;}public Object[] _fieldsToList() { return new Object[] {objects}; }
Fail(Throwable cause, Object... objects) {
super(cause);
this.objects = objects;
}
public String toString() { return joinNemptiesWithColon("Fail", commaCombine(getCause(), objects)); }
}
static class Pair implements Comparable> {
A a;
B b;
Pair() {}
Pair(A a, B b) {
this.b = b;
this.a = a;}
public int hashCode() {
return hashCodeFor(a) + 2*hashCodeFor(b);
}
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Pair)) return false;
Pair t = (Pair) o;
return eq(a, t.a) && eq(b, t.b);
}
public String toString() {
return "<" + a + ", " + b + ">";
}
public int compareTo(Pair p) {
if (p == null) return 1;
int i = ((Comparable) a).compareTo(p.a);
if (i != 0) return i;
return ((Comparable) b).compareTo(p.b);
}
}
static interface IFieldsToList {
Object[] _fieldsToList();
}
static interface ISetter {
void set(A a);
}
static interface IAutoCloseableF0 extends IF0, AutoCloseable {}
static interface IVar extends IF0 {
void set(A a);
A get();
default boolean has() { return get() != null; }
default void clear() { set(null); }
}
static boolean isAbstract(Class c) {
return (c.getModifiers() & Modifier.ABSTRACT) != 0;
}
static boolean isAbstract(Method m) {
return (m.getModifiers() & Modifier.ABSTRACT) != 0;
}
static boolean reflection_isForbiddenMethod(Method m) {
return m.getDeclaringClass() == Object.class
&& eqOneOf(m.getName(), "finalize", "clone", "registerNatives");
}
static Set allInterfacesImplementedBy(Class c) {
if (c == null) return null;
HashSet set = new HashSet();
allInterfacesImplementedBy_find(c, set);
return set;
}
static void allInterfacesImplementedBy_find(Class c, Set set) {
if (c.isInterface() && !set.add(c)) return;
do {
for (Class intf : c.getInterfaces())
allInterfacesImplementedBy_find(intf, set);
} while ((c = c.getSuperclass()) != null);
}
static Method findMethod(Object o, String method, Object... args) {
return findMethod_cached(o, method, args);
}
static boolean findMethod_checkArgs(Method m, Object[] args, boolean debug) {
Class>[] types = m.getParameterTypes();
if (types.length != args.length) {
if (debug)
System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
return false;
}
for (int i = 0; i < types.length; i++)
if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
if (debug)
System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
return false;
}
return true;
}
static Method findStaticMethod(Class c, String method, Object... args) {
Class _c = c;
while (c != null) {
for (Method m : c.getDeclaredMethods()) {
if (!m.getName().equals(method))
continue;
if ((m.getModifiers() & Modifier.STATIC) == 0 || !findStaticMethod_checkArgs(m, args))
continue;
return m;
}
c = c.getSuperclass();
}
return null;
}
static boolean findStaticMethod_checkArgs(Method m, Object[] args) {
Class>[] types = m.getParameterTypes();
if (types.length != args.length)
return false;
for (int i = 0; i < types.length; i++)
if (!(args[i] == null || isInstanceX(types[i], args[i])))
return false;
return true;
}
static void clearPopupMenu(final JPopupMenu menu) {
if (menu != null) { swing(new Runnable() { public void run() { try { menu.removeAll();
} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "menu.removeAll();"; }}); }
}
static String unquote(String s) {
if (s == null) return null;
if (startsWith(s, '[')) {
int i = 1;
while (i < s.length() && s.charAt(i) == '=') ++i;
if (i < s.length() && s.charAt(i) == '[') {
String m = s.substring(1, i);
if (s.endsWith("]" + m + "]"))
return s.substring(i+1, s.length()-i-1);
}
}
if (s.length() > 1) {
char c = s.charAt(0);
if (c == '\"' || c == '\'') {
int l = endsWith(s, c) ? s.length()-1 : s.length();
StringBuilder sb = new StringBuilder(l-1);
for (int i = 1; i < l; i++) {
char ch = s.charAt(i);
if (ch == '\\') {
char nextChar = (i == l - 1) ? '\\' : s.charAt(i + 1);
// Octal escape?
if (nextChar >= '0' && nextChar <= '7') {
String code = "" + nextChar;
i++;
if ((i < l - 1) && s.charAt(i + 1) >= '0'
&& s.charAt(i + 1) <= '7') {
code += s.charAt(i + 1);
i++;
if ((i < l - 1) && s.charAt(i + 1) >= '0'
&& s.charAt(i + 1) <= '7') {
code += s.charAt(i + 1);
i++;
}
}
sb.append((char) Integer.parseInt(code, 8));
continue;
}
switch (nextChar) {
case '\"': ch = '\"'; break;
case '\\': ch = '\\'; break;
case 'b': ch = '\b'; break;
case 'f': ch = '\f'; break;
case 'n': ch = '\n'; break;
case 'r': ch = '\r'; break;
case 't': ch = '\t'; break;
case '\'': ch = '\''; break;
// Hex Unicode: u????
case 'u':
if (i >= l - 5) {
ch = 'u';
break;
}
int code = Integer.parseInt(
"" + s.charAt(i + 2) + s.charAt(i + 3)
+ s.charAt(i + 4) + s.charAt(i + 5), 16);
sb.append(Character.toChars(code));
i += 5;
continue;
default:
ch = nextChar; // added by Stefan
}
i++;
}
sb.append(ch);
}
return sb.toString();
}
}
return s; // not quoted - return original
}
static List quoteAll(Collection l) {
List x = new ArrayList();
for (String s : l)
x.add(quote(s));
return x;
}
static int _hashCode(Object a) {
return a == null ? 0 : a.hashCode();
}
static ArrayList toList(A[] a) { return asList(a); }
static ArrayList toList(int[] a) { return asList(a); }
static ArrayList toList(Set s) { return asList(s); }
static ArrayList toList(Iterable s) { return asList(s); }
static boolean arraysEqual(Object[] a, Object[] b) {
if (a.length != b.length) return false;
for (int i = 0; i < a.length; i++)
if (neq(a[i], b[i])) return false;
return true;
}
static Object metaGet(IMeta o, Object key) {
return metaMapGet(o, key);
}
static Object metaGet(Object o, Object key) {
return metaMapGet(o, key);
}
static Object metaGet(String key, IMeta o) {
return metaMapGet(o, key);
}
static Object metaGet(String key, Object o) {
return metaMapGet(o, key);
}
static Object metaMapGet(IMeta o, Object key) {
return o == null ? null : o.metaGet(key); // We now let the object itself do it (overridable!)
}
static Object metaMapGet(Object o, Object key) {
return metaMapGet(toIMeta(o), key);
}
static void metaPut(IMeta o, Object key, Object value) {
metaMapPut(o, key, value);
}
static void metaPut(Object o, Object key, Object value) {
metaMapPut(o, key, value);
}
static Map convertObjectMetaToMap(IMeta o) { return convertObjectMetaToMap(o, () -> synchroLinkedHashMap()); }
static Map convertObjectMetaToMap(IMeta o, IF0