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);
}
/**
* 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()); }
}
// 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 BetterLabel extends JLabel {
boolean autoToolTip = true;
BetterLabel() {
// Listeners given out to componentPopupMenu must not directly
// reference the outer object (-> weak map problem).
final WeakReference < BetterLabel > me = new WeakReference<>(this);
componentPopupMenu(this, BetterLabel_menuItems(me));
}
BetterLabel(String text) {
this();
this.setText(text);
}
public void setText(String text) {
super.setText(text);
if (autoToolTip)
if (!swic(text, "")) // HTML labels make super-huge, confusing tool tips
setToolTipText(nullIfEmpty(text));
}
}
// moved outside of class for GC reasons (see above)
static VF1 BetterLabel_menuItems(final WeakReference me) {
return new VF1() { public void get(JPopupMenu menu) { try {
addMenuItem(menu, "Copy text to clipboard", new Runnable() { public void run() { try {
copyTextToClipboard(me.get().getText());
} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "copyTextToClipboard(me.get().getText());"; }});
} catch (Exception __e) { throw rethrow(__e); } }
public String toString() { return "addMenuItem(menu, \"Copy text to clipboard\", r {\r\n copyTextToClipboard(me..."; }};
}
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 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 Iterator iterator(Iterable c) {
return c == null ? emptyIterator() : c.iterator();
}
static UnsupportedOperationException unsupportedOperation() {
throw new UnsupportedOperationException();
}
static boolean swic(String a, String b) {
return startsWithIgnoreCase(a, b);
}
static boolean swic(String a, String b, Matches m) {
if (!swic(a, b)) return false;
m.m = new String[] {substring(a, l(b))};
return true;
}
static A setToolTipText(final A c, final Object toolTip) {
if (c == null) return null;
{ swing(new Runnable() { public void run() { try {
String s = nullIfEmpty(str(toolTip));
if (neq(s, c.getToolTipText()))
c.setToolTipText(s);
} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "String s = nullIfEmpty(str(toolTip));\r\n if (neq(s, c.getToolTipText()))\r\n ..."; }}); }
return c;
}
static A setToolTipText(Object toolTip, A c) {
return setToolTipText(c, toolTip);
}
static String nullIfEmpty(String s) {
return isEmpty(s) ? null : s;
}
static Map nullIfEmpty(Map map) {
return isEmpty(map) ? null : map;
}
static List nullIfEmpty(List l) {
return isEmpty(l) ? null : l;
}
static Map putAll(Map a, Map extends A,? extends B> b) {
if (a != null && b != null) a.putAll(b);
return a;
}
static MultiMap putAll(MultiMap a, Map extends A,? extends B> b) {
if (a != null) a.putAll((Map) b);
return a;
}
static Map putAll(Map a, Object... b) {
if (a != null)
litmap_impl(a, b);
return a;
}
static void remove(List l, int i) {
if (l != null && i >= 0 && i < l(l))
l.remove(i);
}
static void remove(Collection l, A a) {
if (l != null) l.remove(a);
}
static B remove(Map map, Object a) {
return map == null ? null : map.remove(a);
}
static void remove(BitSet bs, int i) {
bs.clear(i);
}
static A getAndClear(IVar v) {
A a = v.get();
v.set(null);
return a;
}
static Set keySet(Map map) {
return map == null ? new HashSet() : map.keySet();
}
static Set keySet(Object map) {
return keys((Map) map);
}
static Set keySet(MultiMap mm) {
return mm.keySet();
}
static Set keys(Map map) {
return map == null ? new HashSet() : map.keySet();
}
// convenience shortcut for keys_gen
static Set keys(Object map) {
return keys((Map) map);
}
static Set keys(MultiMap mm) {
return mm.keySet();
}
static int keysSize(MultiMap mm) {
return lKeys(mm);
}
static A reverseGet(List l, int idx) {
if (l == null || idx < 0) return null;
int n = l(l);
return idx < n ? l.get(n-1-idx) : null;
}
static Map cloneMap(Map map) {
if (map == null) return new HashMap();
// assume mutex is equal to map
synchronized(map) {
return map instanceof TreeMap ? new TreeMap((TreeMap) map) // copies comparator
: map instanceof LinkedHashMap ? new LinkedHashMap(map)
: new HashMap(map);
}
}
static List cloneMap(Iterable l, IF1 f) {
List x = emptyList(l);
if (l != null) for (A o : cloneList(l))
x.add(f.get(o));
return x;
}
static Collection values(Map map) {
return map == null ? emptyList() : map.values();
}
// convenience shortcut for values_gen
static Collection values(Object map) {
return values((Map) map);
}
static Collection values(MultiMap mm) {
return mm == null ? emptyList() : concatLists(values(mm.data));
}
static List concatLists(Iterable... lists) {
List l = new ArrayList();
if (lists != null) for (Iterable list : lists)
addAll(l, list);
return l;
}
static List concatLists(Collection extends Iterable> lists) {
List l = new ArrayList();
if (lists != null) for (Iterable list : lists)
addAll(l, list);
return l;
}
static Method hashMap_findKey_method;
static A hashMap_findKey(HashMap map, Object key) { try {
if (hashMap_findKey_method == null)
hashMap_findKey_method = findMethodNamed(HashMap.class, "getNode");
Map.Entry entry = (Map.Entry) hashMap_findKey_method.invoke(map, hashMap_internalHash(key), key);
// java.util.Map.Entry entry = (java.util.Map.Entry) call(hash, 'getNode, hashMap_internalHash(key), wkey);
return entry == null ? null : entry.getKey();
} catch (Exception __e) { throw rethrow(__e); } }
static String joinNemptiesWithColon(String... strings) {
return joinNempties(": ", strings);
}
static String joinNemptiesWithColon(Collection strings) {
return joinNempties(": ", strings);
}
static String commaCombine(Object... l) {
return joinNemptiesWithComma(flattenCollectionsAndArrays(ll(l)));
}
static int hashCodeFor(Object a) {
return a == null ? 0 : a.hashCode();
}
static A set(A o, String field, Object value) {
if (o == null) return null;
if (o instanceof Class) set((Class) o, field, value);
else try {
Field f = set_findField(o.getClass(), field);
makeAccessible(f);
smartSet(f, o, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
return o;
}
static void set(Class c, String field, Object value) {
if (c == null) return;
try {
Field f = set_findStaticField(c, field);
makeAccessible(f);
smartSet(f, null, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static Field set_findStaticField(Class> c, String field) {
Class _c = c;
do {
for (Field f : _c.getDeclaredFields())
if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0)
return f;
_c = _c.getSuperclass();
} while (_c != null);
throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
}
static Field set_findField(Class> c, String field) {
Class _c = c;
do {
for (Field f : _c.getDeclaredFields())
if (f.getName().equals(field))
return f;
_c = _c.getSuperclass();
} while (_c != null);
throw new RuntimeException("Field '" + field + "' not found in " + c.getName());
}
static void set(BitSet bs, int idx) {
{ if (bs != null) bs.set(idx); }
}
static Method findMethod_cached(Object o, String method, Object... args) { try {
if (o == null) return null;
if (o instanceof Class) {
_MethodCache cache = callOpt_getCache((Class) o);
List methods = cache.cache.get(method);
if (methods != null) for (Method m : methods)
if (isStaticMethod(m) && findMethod_checkArgs(m, args, false))
return m;
return null;
} else {
_MethodCache cache = callOpt_getCache(o.getClass());
List methods = cache.cache.get(method);
if (methods != null) for (Method m : methods)
if (findMethod_checkArgs(m, args, false))
return m;
return null;
}
} catch (Exception __e) { throw rethrow(__e); } }
static String quote(Object o) {
if (o == null) return "null";
return quote(str(o));
}
static String quote(String s) {
if (s == null) return "null";
StringBuilder out = new StringBuilder((int) (l(s)*1.5+2));
quote_impl(s, out);
return out.toString();
}
static void quote_impl(String s, StringBuilder out) {
out.append('"');
int l = s.length();
for (int i = 0; i < l; i++) {
char c = s.charAt(i);
if (c == '\\' || c == '"')
out.append('\\').append(c);
else if (c == '\r')
out.append("\\r");
else if (c == '\n')
out.append("\\n");
else if (c == '\t')
out.append("\\t");
else if (c == '\0')
out.append("\\0");
else
out.append(c);
}
out.append('"');
}
static Iterator emptyIterator() {
return Collections.emptyIterator();
}
static boolean startsWithIgnoreCase(String a, String b) {
return regionMatchesIC(a, 0, b, 0, b.length());
}
static boolean isEmpty(Collection c) {
return c == null || c.isEmpty();
}
static boolean isEmpty(CharSequence s) {
return s == null || s.length() == 0;
}
static boolean isEmpty(Object[] a) { return a == null || a.length == 0; }
static boolean isEmpty(byte[] a) { return a == null || a.length == 0; }
static boolean isEmpty(Map map) {
return map == null || map.isEmpty();
}
static HashMap litmap(Object... x) {
HashMap map = new HashMap();
litmap_impl(map, x);
return map;
}
static void litmap_impl(Map map, Object... x) {
if (x != null) for (int i = 0; i < x.length-1; i += 2)
if (x[i+1] != null)
map.put(x[i], x[i+1]);
}
static int lKeys(MultiMap mm) {
return mm == null ? 0 : mm.keysSize();
}
static void addAll(Collection c, Iterable b) {
if (c != null && b != null) for (A a : b) c.add(a);
}
static boolean addAll(Collection c, Collection b) {
return c != null && b != null && c.addAll(b);
}
static boolean addAll(Collection c, B... b) {
return c != null && b != null && c.addAll(Arrays.asList(b));
}
static Map addAll(Map a, Map extends A,? extends B> b) {
if (a != null && b != null) a.putAll(b);
return a;
}
// This is a bit rough... finds static and non-static methods.
static Method findMethodNamed(Object obj, String method) {
if (obj == null) return null;
if (obj instanceof Class)
return findMethodNamed((Class) obj, method);
return findMethodNamed(obj.getClass(), method);
}
static Method findMethodNamed(Class c, String method) {
while (c != null) {
for (Method m : c.getDeclaredMethods())
if (m.getName().equals(method)) {
makeAccessible(m);
return m;
}
c = c.getSuperclass();
}
return null;
}
static int hashMap_internalHash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
static String joinNempties(String sep, Object... strings) {
return joinStrings(sep, strings);
}
static String joinNempties(String sep, Iterable strings) {
return joinStrings(sep, strings);
}
static String joinNemptiesWithComma(Object... strings) {
return joinNempties(", ", strings);
}
static String joinNemptiesWithComma(Iterable strings) {
return joinNempties(", ", strings);
}
static List flattenCollectionsAndArrays(Iterable a) {
List l = new ArrayList();
for (Object x : a)
if (x instanceof Collection)
l.addAll(flattenCollectionsAndArrays((Collection) x));
else if (x instanceof Object[])
l.addAll(flattenCollectionsAndArrays(asList((Object[]) x)));
else
l.add(x);
return l;
}
static void smartSet(Field f, Object o, Object value) throws Exception {
try {
f.set(o, value);
} catch (Exception e) {
Class type = f.getType();
// take care of common case (long to int)
if (type == int.class && value instanceof Long)
{ f.set(o, ((Long) value).intValue()); return; }
if (type == boolean.class && value instanceof String)
{ f.set(o, isTrueOrYes(((String) value))); return; }
if (type == LinkedHashMap.class && value instanceof Map)
{ f.set(o, asLinkedHashMap((Map) value)); return; }
throw e;
}
}
static String joinStrings(String sep, Object... strings) {
return joinStrings(sep, Arrays.asList(strings));
}
static String joinStrings(String sep, Iterable strings) {
StringBuilder buf = new StringBuilder();
for (Object o : unnull(strings)) {
String s = strOrNull(o);
if (nempty(s)) {
if (nempty(buf)) buf.append(sep);
buf.append(s);
}
}
return str(buf);
}
static boolean isTrueOrYes(Object o) {
return isTrueOpt(o) || o instanceof String && (eqicOneOf(((String) o), "1", "t", "true") || isYes(((String) o)));
}
static LinkedHashMap asLinkedHashMap(Map map) {
if (map instanceof LinkedHashMap) return (LinkedHashMap) map;
LinkedHashMap m = new LinkedHashMap();
if (map != null) synchronized(collectionMutex(map)) {
m.putAll(map);
}
return m;
}
static boolean isTrueOpt(Object o) {
if (o instanceof Boolean)
return ((Boolean) o).booleanValue();
return false;
}
static boolean isTrueOpt(String field, Object o) {
return isTrueOpt(getOpt(field, o));
}
static boolean eqicOneOf(String s, String... l) {
for (String x : l) if (eqic(s, x)) return true; return false;
}
static List isYes_yesses = litlist("y", "yes", "yeah", "y", "yup", "yo", "corect", "sure", "ok", "afirmative"); // << collapsed words, so "corect" means "correct"
static boolean isYes(String s) {
return isYes_yesses.contains(collapseWord(toLowerCase(firstWord2(s))));
}
static ArrayList litlist(A... a) {
ArrayList l = new ArrayList(a.length);
for (A x : a) l.add(x);
return l;
}
static String collapseWord(String s) {
if (s == null) return "";
StringBuilder buf = new StringBuilder();
for (int i = 0; i < l(s); i++)
if (i == 0 || !charactersEqualIC(s.charAt(i), s.charAt(i-1)))
buf.append(s.charAt(i));
return buf.toString();
}
static List toLowerCase(List strings) {
List x = new ArrayList();
for (String s : strings)
x.add(s.toLowerCase());
return x;
}
static String[] toLowerCase(String[] strings) {
String[] x = new String[l(strings)];
for (int i = 0; i < l(strings); i++)
x[i] = strings[i].toLowerCase();
return x;
}
static String toLowerCase(String s) {
return s == null ? "" : s.toLowerCase();
}
static String firstWord2(String s) {
s = xltrim(s);
if (empty(s)) return "";
if (isLetterOrDigit(first(s)))
return takeCharsWhile(__38 -> isLetterOrDigit(__38), s);
else return "" + first(s);
}
static boolean charactersEqualIC(char c1, char c2) {
if (c1 == c2) return true;
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
if (u1 == u2) return true;
return Character.toLowerCase(u1) == Character.toLowerCase(u2);
}
static String xltrim(String s) {
int i = 0, n = l(s);
while (i < n && contains(" \t\r\n", s.charAt(i)))
++i;
return substr(s, i);
}
// pred: char -> bool
static String takeCharsWhile(String s, Object pred) {
int i = 0;
while (i < l(s) && isTrue(callF(pred, s.charAt(i)))) ++i;
return substring(s, 0, i);
}
static String takeCharsWhile(IF1 f, String s) {
return takeCharsWhile(s, f);
}
static boolean contains(Collection c, Object o) {
return c != null && c.contains(o);
}
static boolean contains(Iterable it, Object a) {
if (it != null)
for (Object o : it)
if (eq(a, o))
return true;
return false;
}
static boolean contains(Object[] x, Object o) {
if (x != null)
for (Object a : x)
if (eq(a, o))
return true;
return false;
}
static boolean contains(String s, char c) {
return s != null && s.indexOf(c) >= 0;
}
static boolean contains(String s, String b) {
return s != null && s.indexOf(b) >= 0;
}
static boolean contains(BitSet bs, int i) {
return bs != null && bs.get(i);
}
static String substr(String s, int x) {
return substring(s, x);
}
static String substr(String s, int x, int y) {
return substring(s, x, y);
}
}