------------------------------------------------------------------------------- >Log Session: Monday, March 10, 2014 3:50:55 PM CDT >System Info: Product Version = NetBeans IDE 8.0 (Build 201403052200) (#b2429dbb1ff8) Operating System = Windows 7 version 6.1 running on amd64 Java; VM; Vendor = 1.8.0; Java HotSpot(TM) 64-Bit Server VM 25.0-b70; Oracle Corporation Runtime = Java(TM) SE Runtime Environment 1.8.0-b132 Java Home = C:\Developer\Java\jdk1.8.0\jre System Locale; Encoding = en_US (nb); Cp1252 Home Directory = C:\Users\User06 Current Directory = C:\Developer\netbeans-8.0-201403052200\bin User Directory = C:\Users\User06\AppData\Roaming\NetBeans\8.0-201403052200 Cache Directory = C:\Users\User06\AppData\Local\NetBeans\Cache\8.0-201403052200 Installation = C:\Developer\netbeans-8.0-201403052200\nb C:\Developer\netbeans-8.0-201403052200\ergonomics C:\Developer\netbeans-8.0-201403052200\ide C:\Developer\netbeans-8.0-201403052200\extide C:\Developer\netbeans-8.0-201403052200\java C:\Developer\netbeans-8.0-201403052200\apisupport C:\Developer\netbeans-8.0-201403052200\webcommon C:\Developer\netbeans-8.0-201403052200\websvccommon C:\Developer\netbeans-8.0-201403052200\enterprise C:\Developer\netbeans-8.0-201403052200\mobility C:\Developer\netbeans-8.0-201403052200\profiler C:\Developer\netbeans-8.0-201403052200\php C:\Developer\netbeans-8.0-201403052200\harness C:\Developer\netbeans-8.0-201403052200\cnd C:\Developer\netbeans-8.0-201403052200\dlight C:\Developer\netbeans-8.0-201403052200\groovy C:\Developer\netbeans-8.0-201403052200\javacard C:\Developer\netbeans-8.0-201403052200\javafx C:\Developer\netbeans-8.0-201403052200\platform Boot & Ext. Classpath = C:\Developer\Java\jdk1.8.0\jre\lib\resources.jar;C:\Developer\Java\jdk1.8.0\jre\lib\rt.jar;C:\Developer\Java\jdk1.8.0\jre\lib\sunrsasign.jar;C:\Developer\Java\jdk1.8.0\jre\lib\jsse.jar;C:\Developer\Java\jdk1.8.0\jre\lib\jce.jar;C:\Developer\Java\jdk1.8.0\jre\lib\charsets.jar;C:\Developer\Java\jdk1.8.0\jre\lib\jfr.jar;C:\Developer\Java\jdk1.8.0\jre\classes;C:\Developer\Java\jdk1.8.0\jre\lib\ext\access-bridge-64.jar;C:\Developer\Java\jdk1.8.0\jre\lib\ext\cldrdata.jar;C:\Developer\Java\jdk1.8.0\jre\lib\ext\dnsns.jar;C:\Developer\Java\jdk1.8.0\jre\lib\ext\jaccess.jar;C:\Developer\Java\jdk1.8.0\jre\lib\ext\jfxrt.jar;C:\Developer\Java\jdk1.8.0\jre\lib\ext\localedata.jar;C:\Developer\Java\jdk1.8.0\jre\lib\ext\nashorn.jar;C:\Developer\Java\jdk1.8.0\jre\lib\ext\sunec.jar;C:\Developer\Java\jdk1.8.0\jre\lib\ext\sunjce_provider.jar;C:\Developer\Java\jdk1.8.0\jre\lib\ext\sunmscapi.jar;C:\Developer\Java\jdk1.8.0\jre\lib\ext\sunpkcs11.jar;C:\Developer\Java\jdk1.8.0\jre\lib\ext\zipfs.jar Application Classpath = C:\Developer\netbeans-8.0-201403052200\platform\lib\boot.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\org-openide-modules.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\org-openide-util-lookup.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\org-openide-util.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\boot_ja.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\boot_pt_BR.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\boot_ru.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\boot_zh_CN.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-modules_ja.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-modules_pt_BR.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-modules_ru.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-modules_zh_CN.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-util-lookup_ja.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-util-lookup_pt_BR.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-util-lookup_ru.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-util-lookup_zh_CN.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-util_ja.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-util_pt_BR.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-util_ru.jar;C:\Developer\netbeans-8.0-201403052200\platform\lib\locale\org-openide-util_zh_CN.jar;C:/Developer/Java/jdk1.8.0\lib\dt.jar;C:/Developer/Java/jdk1.8.0\lib\tools.jar Startup Classpath = C:\Developer\netbeans-8.0-201403052200\platform\core\core.jar;C:\Developer\netbeans-8.0-201403052200\platform\core\org-openide-filesystems.jar;C:\Developer\netbeans-8.0-201403052200\platform\core\locale\core_ja.jar;C:\Developer\netbeans-8.0-201403052200\platform\core\locale\core_pt_BR.jar;C:\Developer\netbeans-8.0-201403052200\platform\core\locale\core_ru.jar;C:\Developer\netbeans-8.0-201403052200\platform\core\locale\core_zh_CN.jar;C:\Developer\netbeans-8.0-201403052200\platform\core\locale\org-openide-filesystems_ja.jar;C:\Developer\netbeans-8.0-201403052200\platform\core\locale\org-openide-filesystems_pt_BR.jar;C:\Developer\netbeans-8.0-201403052200\platform\core\locale\org-openide-filesystems_ru.jar;C:\Developer\netbeans-8.0-201403052200\platform\core\locale\org-openide-filesystems_zh_CN.jar;C:\Developer\netbeans-8.0-201403052200\nb\core\org-netbeans-upgrader.jar;C:\Developer\netbeans-8.0-201403052200\nb\core\locale\core_nb.jar;C:\Developer\netbeans-8.0-201403052200\nb\core\locale\core_nb_ja.jar;C:\Developer\netbeans-8.0-201403052200\nb\core\locale\core_nb_pt_BR.jar;C:\Developer\netbeans-8.0-201403052200\nb\core\locale\core_nb_ru.jar;C:\Developer\netbeans-8.0-201403052200\nb\core\locale\core_nb_zh_CN.jar;C:\Developer\netbeans-8.0-201403052200\nb\core\locale\org-netbeans-upgrader_ja.jar;C:\Developer\netbeans-8.0-201403052200\nb\core\locale\org-netbeans-upgrader_pt_BR.jar;C:\Developer\netbeans-8.0-201403052200\nb\core\locale\org-netbeans-upgrader_ru.jar;C:\Developer\netbeans-8.0-201403052200\nb\core\locale\org-netbeans-upgrader_zh_CN.jar ------------------------------------------------------------------------------- INFO [org.netbeans.modules.netbinox]: Install area set to file:/C:/Developer/netbeans-8.0-201403052200/ WARNING [org.netbeans.core.modules]: the modules [org.netbeans.modules.form.nb] use org.jdesktop.layout which is deprecated: Use javax.swing.GroupLayout instead. (In form editor: select Form ... in Inspector; change Layout Generation Style to Standard Java 6 code.) WARNING [org.netbeans.core.modules]: the modules [org.netbeans.modules.java.editor.lib, org.netbeans.modules.xml.text] use org.netbeans.modules.editor.deprecated.pre65formatting which is deprecated. WARNING [org.netbeans.core.modules]: the modules [org.netbeans.modules.ide.kit, org.netbeans.modules.xml.text] use org.netbeans.modules.editor.structure which is deprecated. WARNING [org.netbeans.core.modules]: the modules [org.netbeans.modules.java.hints, org.netbeans.modules.maven.hints] use org.netbeans.modules.java.hints.legacy.spi which is deprecated: Use Java Hints SPI (org.netbeans.spi.java.hints) instead. INFO [org.netbeans.core.startup.NbEvents]: Turning on modules: org.openide.util.lookup [8.24.1 201403052200] org.openide.util [8.37.1 201403052200] org.openide.modules [7.42.1 201403052200] org.netbeans.api.annotations.common/1 [1.23.1 201403052200] org.openide.filesystems [8.10.1 201403052200] org.openide.awt [7.61.1 201403052200] org.netbeans.api.progress/1 [1.37.1 201403052200] org.openide.dialogs [7.36.1 201403052200] org.openide.nodes [7.38.1 201403052200] org.openide.windows [6.70.1 201403052200] org.netbeans.modules.editor.mimelookup/1 [1.35.1 201403052200] org.openide.text [6.61.1 201403052200] org.netbeans.swing.tabcontrol [1.50.1 201403052200] org.netbeans.swing.outline [1.29.1 201403052200] org.openide.explorer [6.56.1 201403052200] org.openide.actions [6.34.1 201403052200] org.netbeans.modules.queries/1 [1.38.1 201403052200] org.openide.loaders [7.56.2 201403052200] org.openide.io [1.44.1 201403052200] org.openide.execution [1.35.1 201403052200] org.netbeans.upgrader [4.30.1 201403052200] org.netbeans.swing.plaf [1.36.1 201403052200] org.netbeans.modules.projectapi/1 [1.57.1 201403052200] org.netbeans.swing.dirchooser [1.24.1 201403052200] org.netbeans.spi.viewmodel/2 [1.45.1 201403052200] org.netbeans.spi.tasklist/1 [1.29.1.1 1 201403052200] org.netbeans.spi.quicksearch [1.22.1 201403052200] org.netbeans.spi.palette/1 [1.41.1 201403052200] org.netbeans.spi.navigator/1 [1.32.1 201403052200] org.netbeans.modules.editor.settings/1 [1.52.1 201403052200] org.netbeans.modules.editor.util/1 [1.57.1 201403052200] org.netbeans.modules.keyring [1.19.1 201403052200] org.netbeans.bootstrap/1 [2.67.1 201403052200] org.netbeans.core.startup/1 [1.53.1 201403052200] org.netbeans.modules.sampler [1.9.1 201403052200] org.netbeans.core/2 [3.45.1 201403052200] org.netbeans.modules.options.api/1 [1.39.1 201403052200] org.netbeans.modules.options.keymap [1.30.1 201403052200] org.netbeans.modules.editor.settings.storage/1 [1.44.1 201403052200] org.netbeans.modules.lexer/2 [1.58.1.1 1 201403052200] org.netbeans.modules.editor.fold/1 [1.39.1 201403052200] org.netbeans.modules.editor.lib2/1 [1.84.1.43 43 201403052200] org.netbeans.modules.editor.indent/2 [1.36.1 201403052200] org.netbeans.modules.editor.lib/3 [3.48.1.22.43 22 201403052200] org.netbeans.modules.editor.fold.nbui [1.6.1.43 201403052200] org.netbeans.modules.editor/3 [1.78.1.5.22.43 5 201403052200] org.netbeans.libs.javacapi [8.10.1.3 3 201403052200] org.netbeans.libs.javacimpl/1 [0.37.1.25 25 201403052200] org.netbeans.lib.nbjavac [1.12.1.2.25 2 201403052200] org.netbeans.modules.editor.errorstripe.api/1 [2.28.1.1 1 201403052200] org.netbeans.api.java.classpath/1 [1.41.1 201403052200] org.netbeans.api.java/1 [1.51.1 201403052200] org.netbeans.modules.java.platform/1 [1.34.1 201403052200] org.netbeans.modules.options.editor/1 [1.50.1 201403052200] org.netbeans.modules.editor.errorstripe/2 [2.29.1.1.1 1 201403052200] org.netbeans.modules.diff/1 [1.46.1.42.1 42 201403052200] org.netbeans.modules.editor.guards/1 [1.25.1 201403052200] org.netbeans.modules.project.indexingbridge [1.12.1 201403052200] org.netbeans.modules.masterfs/2 [2.46.1 201403052200] org.netbeans.libs.lucene/3 [3.15.1 201403052200] org.netbeans.modules.parsing.lucene/2 [2.27.2.1 1 201403052200] org.netbeans.modules.projectuiapi/1 [1.76.1.8 8 201403052200] org.netbeans.modules.parsing.api/1 [1.73.1.8 8 201403052200] org.netbeans.modules.refactoring.api [1.39.1.1 1 201403052200] org.netbeans.modules.classfile/1 [1.44.1 201403052200] org.netbeans.modules.editor.indent.project/0 [1.17.1 201403052200] org.netbeans.core.multiview/1 [1.39.1 201403052200] org.netbeans.modules.java.lexer/1 [1.27.1 201403052200] org.netbeans.modules.java.preprocessorbridge [1.34.1.1 1 201403052200] org.netbeans.modules.java.source [0.134.1.31.2.25.8 31 201403052200] org.netbeans.modules.project.libraries/1 [1.46.1 201403052200] org.apache.xml.resolver [1.27.1.12 12 201403052200] org.netbeans.api.xml/1 [1.38.1 201403052200] org.netbeans.modules.xml.catalog/2 [1.36.1.4 4 201403052200] org.netbeans.modules.project.ant/1 [1.59.1 201403052200] org.netbeans.api.search [1.16.1 201403052200] org.netbeans.modules.options.java [1.9.1 201403052200] org.apache.tools.ant.module/3 [3.74.1.2 2 201403052200] org.netbeans.modules.java.project/1 [1.58.1 201403052200] org.netbeans.modules.settings/1 [1.44.1 201403052200] org.netbeans.spi.editor.hints/0 [1.34.1.7.43 7 201403052200] org.netbeans.modules.autoupdate.services [1.45.1 201403052200] org.netbeans.modules.autoupdate.ui [1.40.1 201403052200] org.netbeans.core.windows/2 [2.75.1 201403052200] org.netbeans.modules.code.analysis/0 [1.20.2 201403052200] org.netbeans.spi.java.hints [1.20.1.13.2.25.31.8 13 201403052200] org.netbeans.modules.editor.tools.storage [1.3.1 201403052200] org.netbeans.spi.editor.hints.projects [1.6.1.1.7 1 201403052200] org.netbeans.api.debugger/1 [1.45.1 201403052200] org.netbeans.spi.debugger.ui/1 [2.43.1 201403052200] org.netbeans.modules.xml.core/2 [1.38.1.4 4 201403052200] org.netbeans.modules.xml.xam/1 [1.27.1.1 1 201403052200] org.netbeans.modules.xml.retriever/1 [1.26.1 201403052200] org.netbeans.modules.xml.schema.model/1 [1.27.1.1 1 201403052200] org.netbeans.modules.xml.axi [1.26.1 201403052200] org.netbeans.modules.editor.bracesmatching/0 [1.33.1.43 201403052200] org.netbeans.modules.editor.deprecated.pre65formatting/0 [1.22.1.1.5.22.43 1 201403052200] org.netbeans.modules.editor.completion/1 [1.39.1.2 2 201403052200] org.netbeans.modules.editor.codetemplates/1 [1.37.1.1 1 201403052200] org.netbeans.modules.editor.breadcrumbs/0 [1.11.1 201403052200] org.netbeans.core.ui/1 [1.40.1 201403052200] org.netbeans.modules.jumpto/1 [1.41.1.1 1 201403052200] org.netbeans.modules.editor.actions/1 [1.25.1.43 201403052200] org.netbeans.modules.csl.api/2 [2.46.1.2.1.1.8 2 201403052200] org.netbeans.modules.editor.structure/1 [1.42.1.1 1 201403052200] org.netbeans.modules.xml.lexer [1.25.1 201403052200] org.netbeans.modules.xml.text/2 [1.43.1 201403052200] org.netbeans.modules.xml [1.26.2 201403052200] org.netbeans.modules.servletapi/1 [1.35.1 201403052200] org.netbeans.modules.httpserver/2 [2.31.1.42 42 201403052200] org.netbeans.modules.xsl/1 [1.38.1 201403052200] org.netbeans.modules.xml.xdm/1 [1.28.1.1 1 201403052200] org.netbeans.libs.xerces/1 [1.34.1.281 281 201403052200] org.netbeans.modules.xml.tax/2 [1.38.1.281 201403052200] org.netbeans.modules.xml.tools/2 [1.38.1 201403052200] org.netbeans.modules.xml.tools.java [1.24.1 201403052200] org.netbeans.modules.xml.schema.completion [1.25.1 201403052200] org.netbeans.modules.xml.multiview/1 [1.33.2.2 2 201403052200] org.netbeans.modules.xml.jaxb.api/1 [1.22.1 201403052200] org.netbeans.modules.xml.jaxb/1 [1.25.1 201403052200] org.netbeans.modules.whitelist [1.18.1 201403052200] org.netbeans.core.execution/1 [1.36.1 201403052200] org.netbeans.modules.sendopts/2 [2.30.1 201403052200] org.netbeans.modules.favorites/1 [1.39.1 201403052200] org.netbeans.modules.projectui [1.52.1.8 201403052200] org.netbeans.modules.welcome/1 [1.37.1 201403052200] org.netbeans.modules.websvc.jaxws21api/1 [1.27.1 201403052200] org.netbeans.libs.jaxb/1 [1.27.1 201403052200] org.netbeans.modules.websvc.jaxws21/1 [1.28.1 201403052200] org.netbeans.libs.json_simple/1 [0.8.1 201403052200] org.netbeans.modules.web.webkit.debugging [1.31.1 201403052200] org.netbeans.modules.web.common [1.68.1 201403052200] org.netbeans.modules.web.indent [1.14.1 201403052200] org.netbeans.modules.extexecution/2 [1.41.1 201403052200] org.netbeans.modules.extbrowser/1 [1.47.1 201403052200] org.netbeans.modules.web.browser.api [1.37.1 201403052200] org.netbeans.modules.versioning.core/1 [1.20.1.1.42 1 201403052200] org.netbeans.modules.versioning/1 [1.42.1.1 1 201403052200] org.netbeans.modules.spellchecker.apimodule [1.20.1 201403052200] org.netbeans.modules.versioning.util [1.57.1.42.1 201403052200] org.netbeans.modules.team.commons [1.40.1 201403052200] org.netbeans.modules.versioning.ui/1 [1.17.1.42.1 201403052200] org.netbeans.modules.versioning.system.cvss.installer [1.11.1 201403052200] org.netbeans.modules.versioning.masterfs [1.9.1 201403052200] org.netbeans.modules.versioning.indexingbridge/0 [1.20.1 201403052200] org.netbeans.modules.utilities/1 [1.55.1 201403052200] org.netbeans.modules.utilities.project/1 [1.33.1 201403052200] org.netbeans.modules.javahelp/1 [2.36.1 201403052200] org.netbeans.modules.usersguide/1 [1.44.1 201403052200] org.netbeans.modules.updatecenters/1 [1.35.1 201403052200] org.netbeans.lib.uihandler [1.40.1 201403052200] org.netbeans.modules.uihandler [2.30.1 201403052200] org.netbeans.modules.uihandler.exceptionreporter [1.24.1 201403052200] org.jdesktop.beansbinding/1 [1.23.1.121 121 201403052200] org.apache.commons.logging [1.1.1 1.1.1] org.netbeans.modules.maven.embedder/2 [2.45.1 201403052200] org.netbeans.modules.maven.indexer/2 [2.30.1 201403052200] org.apache.commons.codec [1.3.0 1.3.0] org.netbeans.modules.maven.model/1 [1.37.1 201403052200] org.netbeans.modules.java.sourceui/1 [1.37.1.1.31 1 201403052200] org.netbeans.modules.java.api.common/0 [1.70.1 201403052200] org.netbeans.api.debugger.jpda/2 [2.48.1 201403052200] org.netbeans.modules.swing.validation/2 [1.27.1 201403052200] org.netbeans.modules.maven/2 [2.103.1 201403052200] org.netbeans.modules.gsf.testrunner/1 [1.42.1 201403052200] org.netbeans.modules.gototest/1 [1.26.1 201403052200] org.netbeans.libs.testng/1 [1.10.1 201403052200] org.netbeans.modules.java.testrunner [1.12.1 201403052200] org.netbeans.modules.testng [2.14.1 201403052200] org.netbeans.modules.testng.maven [2.11.1 201403052200] org.netbeans.modules.testng.ant [2.10.1 201403052200] org.netbeans.lib.terminalemulator [1.30.1 201403052200] org.netbeans.modules.terminal [1.18.2 201403052200] org.netbeans.modules.templates/1 [1.2.1 201403052200] org.netbeans.libs.swingx/1 [1.23.1 201403052200] org.netbeans.modules.team.ide [1.8.1 201403052200] org.netbeans.modules.tasklist.ui/1 [1.26.1.1.1 1 201403052200] org.netbeans.modules.tasklist.todo/1 [1.26.1.1 1 201403052200] org.netbeans.modules.tasklist.projectint/1 [1.24.1 201403052200] org.netbeans.modules.tasklist.kit [1.24.1 201403052200] org.netbeans.libs.ini4j/1 [1.26.1 201403052200] org.netbeans.libs.svnClientAdapter/1 [1.35.1 201403052200] org.netbeans.modules.subversion [1.36.1.42.1 201403052200] org.netbeans.libs.cglib/1 [1.22.1 201403052200] org.netbeans.libs.springframework/1 [1.33.1 201403052200] org.netbeans.modules.j2ee.core.utilities/0 [1.28.1 201403052200] org.netbeans.modules.j2ee.metadata/0 [1.27.1 201403052200] org.netbeans.modules.ant.browsetask [1.36.1 201403052200] org.netbeans.modules.ant.debugger/1 [1.32.1.2 201403052200] org.netbeans.modules.ant.freeform/1 [1.41.1 201403052200] org.netbeans.core.ide/1 [1.36.1 201403052200] org.netbeans.core.multitabs/1 [1.7.1.1 1 201403052200] org.netbeans.modules.editor.bookmarks/1 [1.32.1 201403052200] org.netbeans.modules.editor.plain.lib/1 [1.29.1 201403052200] com.jcraft.jsch [0.1.49 0.1.49] org.netbeans.libs.jna/1 [1.35.1 201403052200] org.netbeans.modules.dlight.nativeexecution [1.33.3.1 1 201403052200] org.netbeans.modules.dlight.terminal [1.18.1.1 1 201403052200] org.netbeans.core.multitabs.project [1.5.1.1.1 1 201403052200] org.netbeans.modules.editor.search [1.17.1.0.43 0 201403052200] org.netbeans.modules.languages.diff [1.28.1 201403052200] org.netbeans.modules.html.lexer/1 [1.34.1 201403052200] org.netbeans.modules.html.editor.lib/3 [3.26.1 201403052200] org.netbeans.modules.html.parser/1 [1.28.1.1 1 201403052200] org.netbeans.modules.html.validation/1 [1.25.1 201403052200] org.netbeans.libs.antlr3.runtime [1.17.1.1 1 201403052200] org.netbeans.modules.css.lib/1 [1.69.1 201403052200] org.netbeans.modules.css.model [1.26.1 201403052200] org.netbeans.modules.css.editor/1 [1.60.1 201403052200] org.netbeans.modules.html/1 [1.55.1 201403052200] org.netbeans.modules.html.editor/2 [2.48.1 201403052200] org.netbeans.libs.nashorn/1 [1.15.1 201403052200] org.netbeans.modules.javascript2.editor/1 [0.35.1 201403052200] org.netbeans.modules.javascript2.extjs [1.3.1 201403052200] org.netbeans.modules.javascript2.jquery [1.3.1 201403052200] org.netbeans.modules.javascript2.knockout [1.3.1 201403052200] org.netbeans.modules.javascript2.kit/1 [0.8.1 201403052200] org.netbeans.modules.languages.manifest [1.27.1 201403052200] org.netbeans.core.output2/1 [1.36.1 201403052200] org.netbeans.modules.css.visual/3 [3.26.1 201403052200] org.netbeans.modules.properties/1 [1.49.2 201403052200] org.netbeans.libs.bytelist/1 [0.23.1.1 1 201403052200] org.netbeans.libs.jvyamlb/1 [0.25.1 201403052200] org.netbeans.modules.languages.yaml [2.27.1 201403052200] org.netbeans.modules.image/1 [1.43.1 201403052200] org.netbeans.modules.editor.kit [1.28.1 201403052200] org.netbeans.modules.print [7.21.1 201403052200] org.netbeans.modules.autoupdate.cli [1.8.1 201403052200] org.netbeans.libs.osgi [1.17.1 201403052200] org.netbeans.libs.felix [2.11.1 201403052200] org.netbeans.modules.core.kit [1.25.1 201403052200] org.netbeans.modules.defaults/1 [1.27.1 201403052200] org.netbeans.modules.print.editor [7.22.1 201403052200] org.netbeans.modules.editor.macros/0 [1.26.1.1.22 1 201403052200] org.netbeans.modules.editor.plain/2 [1.30.1 201403052200] org.netbeans.modules.ide.kit [1.28.1 201403052200] org.netbeans.modules.projectui.buildmenu/1 [1.22.1.1 1 201403052200] org.netbeans.modules.ant.kit [1.25.1 201403052200] org.netbeans.modules.i18n/1 [1.48.1 201403052200] org.netbeans.modules.refactoring.java/1 [1.50.1.1 1 201403052200] org.netbeans.modules.j2ee.metadata.model.support/1 [1.26.1 201403052200] org.netbeans.modules.j2ee.persistenceapi/1 [1.29.1.1 1 201403052200] org.netbeans.modules.debugger.jpda.ant [1.33.1 201403052200] org.netbeans.modules.java.j2seproject/1 [1.74.1.48 48 201403052200] org.netbeans.modules.javawebstart [1.27.1 201403052200] org.netbeans.modules.junit/2 [2.63.1 201403052200] org.netbeans.modules.java.freeform/1 [1.38.1 201403052200] org.netbeans.modules.java.j2seembedded [1.4.1 201403052200] org.netbeans.modules.java.debug [1.32.1.1.25.31 1 201403052200] org.netbeans.modules.java.examples/1 [1.31.1 201403052200] org.netbeans.modules.java.hints.ui [1.12.1.1.13 1 201403052200] org.netbeans.modules.java.editor.lib/1 [1.33.1 201403052200] org.netbeans.modules.java.editor/1 [2.59.1.10.1.1 10 201403052200] org.netbeans.modules.java.navigation/1 [1.34.1 201403052200] org.netbeans.modules.findbugs.installer [1.11.1 201403052200] org.netbeans.modules.java.hints.legacy.spi/1 [1.10.1.1.13 1 201403052200] org.netbeans.modules.java.hints/1 [1.75.1.15.10.1.31.1.13 15 201403052200] org.netbeans.modules.java.hints.declarative/1 [1.15.1.1.15.1.13 1 201403052200] org.netbeans.modules.java.j2seprofiles [1.3.1 201403052200] org.netbeans.modules.java.j2sedeploy [1.7.1 201403052200] org.netbeans.modules.java.j2seplatform/1 [1.36.1 201403052200] org.netbeans.modules.javadoc/1 [1.49.1 201403052200] org.netbeans.modules.nashorn.execution/1 [1.1.1 201403052200] org.netbeans.modules.beans/1 [1.45.1.1.10 1 201403052200] org.netbeans.modules.spellchecker.bindings.java/1 [1.25.1 201403052200] org.netbeans.modules.java.source.ant [1.25.1.1.31 1 201403052200] org.netbeans.modules.projectimport.eclipse.core/1 [2.25.1 201403052200] org.netbeans.modules.java.kit [1.28.1 201403052200] org.netbeans.modules.spring.beans/0 [1.34.1.1 1 201403052200] org.netbeans.modules.spellchecker [1.30.1.1.7 1 201403052200] org.netbeans.modules.spellchecker.kit [1.21.1 201403052200] org.netbeans.modules.spellchecker.dictionary_en [1.18.1 201403052200] org.netbeans.modules.properties.syntax/1 [1.45.1 201403052200] org.netbeans.modules.spellchecker.bindings.properties [1.17.1 201403052200] org.netbeans.modules.spellchecker.bindings.htmlxml [1.22.1 201403052200] org.netbeans.modules.server/0 [1.28.1 201403052200] org.netbeans.modules.schema2beans/1 [1.42.1 201403052200] org.netbeans.modules.projectimport.eclipse.j2se [1.23.1.48 201403052200] org.netbeans.modules.progress.ui [1.28.1 201403052200] org.netbeans.modules.profiler.utilities/1 [1.30.1 201403052200] org.netbeans.lib.profiler/1 [1.54.1 201403052200] org.netbeans.lib.profiler.charts/1 [1.23.1 201403052200] org.netbeans.lib.profiler.common/1 [1.30.1 201403052200] org.netbeans.modules.profiler.api/1 [1.17.1 201403052200] org.netbeans.lib.profiler.ui/1 [1.43.1 201403052200] org.netbeans.modules.profiler.attach/2 [2.13.1 201403052200] org.netbeans.modules.profiler.selector.api/1 [1.28.1 201403052200] org.netbeans.modules.profiler.selector.ui/1 [1.25.1 201403052200] org.netbeans.modules.profiler/2 [2.20.1 201403052200] org.netbeans.modules.profiler.ppoints [1.13.1 201403052200] org.netbeans.modules.profiler.stp [1.11.1 201403052200] org.netbeans.modules.profiler.snaptracer/1 [1.16.1 201403052200] org.netbeans.modules.profiler.projectsupport [1.27.1 201403052200] org.netbeans.modules.profiler.oql/2 [2.11.1 201403052200] org.netbeans.modules.profiler.oql.language/0 [0.24.1 201403052200] org.netbeans.modules.profiler.options [1.12.1 201403052200] org.netbeans.modules.profiler.categorization.api/1 [1.12.1 201403052200] org.netbeans.modules.profiler.nbimpl/1 [1.13.1 201403052200] org.netbeans.modules.profiler.nbmodule/1 [1.29.1 201403052200] org.netbeans.modules.profiler.heapwalker [1.98.1 201403052200] org.netbeans.modules.profiler.drilldown/1 [1.11.1 201403052200] org.netbeans.modules.profiler.kit [1.13.1 201403052200] org.netbeans.modules.profiler.j2se/1 [1.30.1 201403052200] org.netbeans.modules.j2eeapis/1 [1.31.1 201403052200] org.netbeans.modules.j2ee.dd/1 [1.38.1.2 2 201403052200] org.netbeans.modules.j2ee.core/0 [1.22.1 201403052200] org.netbeans.api.web.webmodule [1.36.1 201403052200] org.netbeans.modules.j2ee.api.ejbmodule [1.35.1 201403052200] org.netbeans.modules.j2eeserver/4 [1.106.1.1 1 201403052200] org.netbeans.modules.db.metadata.model/1 [1.6.1 201403052200] org.netbeans.modules.db/1 [1.60.1.30 30 201403052200] org.netbeans.modules.dbapi [1.29.1.30 201403052200] org.netbeans.modules.dbschema/1 [1.35.1.4.30 4 201403052200] org.netbeans.modules.javaee.injection [1.2.1 201403052200] org.netbeans.modules.j2ee.eclipselink/1 [1.28.1 201403052200] org.netbeans.modules.j2ee.persistence [1.45.1.1 1 201403052200] org.netbeans.modules.profiler.j2ee/1 [1.30.1 201403052200] org.netbeans.modules.profiler.freeform/1 [1.29.1 201403052200] org.netbeans.modules.parsing.ui [1.9.1 201403052200] org.netbeans.modules.notifications [1.3.1 201403052200] org.netbeans.core.netigso [1.26.1 201403052200] org.netbeans.modules.netbinox [1.34.1 201403052200] org.eclipse.mylyn.commons.net [3.10.0 3.10.0.20131018-1210] org.eclipse.mylyn.tasks.core [3.10.0 3.10.0.20131010-2023] org.eclipse.core.jobs [3.5.101 3.5.101.v20120113-1953] org.eclipse.mylyn.wikitext.core [1.9.0 1.9.0.20131007-2055] org.netbeans.modules.bugtracking [1.101.1 201403052200] org.eclipse.equinox.common [3.6.0 3.6.0.v20110523] org.eclipse.core.runtime [3.7.0 3.7.0.v20110110] org.netbeans.modules.mylyn.util [1.27.1 201403052200] org.netbeans.modules.mercurial [1.36.1.42.1 201403052200] org.netbeans.modules.maven.spring/1 [1.24.1 201403052200] org.netbeans.modules.gsf.codecoverage [1.26.1 201403052200] org.netbeans.modules.maven.coverage [1.18.1 201403052200] org.netbeans.modules.maven.repository/1 [1.37.1 201403052200] org.netbeans.api.visual [2.42.1 201403052200] org.netbeans.modules.maven.graph/1 [1.27.1 201403052200] org.netbeans.modules.maven.grammar/1 [1.41.1.1 201403052200] org.netbeans.modules.maven.osgi/1 [1.29.1 201403052200] org.netbeans.modules.maven.refactoring [1.15.1 201403052200] org.netbeans.modules.maven.checkstyle [1.12.1 201403052200] org.netbeans.modules.maven.kit/1 [4.26.1 201403052200] org.netbeans.modules.maven.search [1.27.1 201403052200] org.netbeans.modules.maven.profiler/1 [1.25.1 201403052200] org.netbeans.modules.maven.persistence/1 [1.24.1 201403052200] org.netbeans.modules.maven.junit/1 [1.27.1 201403052200] org.netbeans.modules.maven.hints/1 [1.35.1 201403052200] org.netbeans.modules.masterfs.windows [1.11.1 201403052200] org.netbeans.modules.masterfs.nio2 [1.11.1 201403052200] org.netbeans.modules.bugtracking.commons [1.2.1 201403052200] org.netbeans.modules.localtasks [1.20.1 201403052200] org.netbeans.modules.localhistory [1.29.1 201403052200] org.netbeans.modules.lexer.nbbridge/1 [1.27.1.1 201403052200] org.netbeans.modules.keyring.fallback [1.3.1 201403052200] org.netbeans.modules.keyring.impl [1.18.1 201403052200] org.netbeans.modules.javascript2.debug/1 [1.2.1 201403052200] org.netbeans.modules.java.source.queries [1.13.1 201403052200] org.netbeans.modules.java.source.queriesimpl [1.12.1 201403052200] org.netbeans.modules.java.metrics [1.3.1 201403052200] org.netbeans.modules.java.guards/0 [0.26.1 201403052200] org.netbeans.modules.j2ee.eclipselinkmodelgen/1 [1.28.1 201403052200] org.netbeans.modules.j2ee.jpa.refactoring [1.26.1 201403052200] org.netbeans.modules.j2ee.jpa.verification [1.30.1 201403052200] org.netbeans.modules.db.dataview [1.24.1 201403052200] org.netbeans.modules.db.core [1.31.1 201403052200] org.netbeans.modules.db.sql.editor [1.31.1.30 201403052200] org.netbeans.modules.db.drivers [1.25.1 201403052200] org.netbeans.modules.db.sql.visualeditor/1 [2.26.1.30 201403052200] org.netbeans.modules.db.mysql [0.23.1.2 2 201403052200] org.netbeans.modules.derby [1.35.1 201403052200] org.netbeans.modules.db.kit [1.26.1 201403052200] org.netbeans.modules.j2ee.persistence.kit [1.27.1 201403052200] org.netbeans.modules.ide.ergonomics [1.24.1 201403052200] org.netbeans.modules.autoupdate.pluginimporter [1.15.1 201403052200] org.netbeans.modules.deadlock.detector [1.1.1 201403052200] org.netbeans.modules.ide.branding/1 [1.28.1 201403052200] org.netbeans.modules.ide.branding.kit [1.24.1 201403052200] org.netbeans.modules.form/2 [1.52.1.4 4 201403052200] org.jdesktop.layout/1 [1.29.1 201403052200] org.netbeans.modules.form.nb/0 [0.13.1.1.4 1 201403052200] org.netbeans.modules.i18n.form/2 [1.45.1.4.1 201403052200] org.netbeans.libs.commons_net/2 [2.16.1.33 33 201403052200] org.netbeans.modules.hudson [2.5.1 201403052200] org.netbeans.modules.hudson.ui [1.5.1 201403052200] org.netbeans.modules.hudson.tasklist [1.12.1 201403052200] org.netbeans.modules.hudson.subversion [1.24.1 201403052200] org.netbeans.modules.hudson.mercurial [1.24.1 201403052200] org.netbeans.modules.hudson.maven [1.24.1 201403052200] org.netbeans.modules.hudson.git [1.13.1 201403052200] org.netbeans.modules.hudson.ant [1.22.1 201403052200] org.netbeans.modules.html.knockout [1.5.1 201403052200] org.netbeans.modules.html.custom [1.1.1 201403052200] org.netbeans.core.browser/1 [1.23.1.1 1 201403052200] org.netbeans.modules.html.angular [1.6.1 201403052200] org.netbeans.libs.freemarker/1 [2.29.1.238 238 201403052200] org.netbeans.modules.hibernate4lib/1 [1.1.1.238 201403052200] org.netbeans.modules.hibernate/1 [1.27.1.1 1 201403052200] org.netbeans.modules.hibernateweb [1.21.1 201403052200] org.eclipse.jgit [3.2.0 3.2.0.201312181205-r] org.netbeans.libs.jna.platform/1 [1.5.1 201403052200] org.netbeans.libs.jsch.agentproxy/1 [0.7.1 201403052200] org.netbeans.libs.git/1 [1.19.1 201403052200] org.netbeans.modules.git [1.14.1.1 201403052200] org.netbeans.modules.form.refactoring/0 [0.12.1.1.4.1 1 201403052200] org.netbeans.modules.form.binding/0 [0.12.1.1.4.1 1 201403052200] org.netbeans.modules.form.kit [1.24.1 201403052200] org.netbeans.modules.form.j2ee/0 [0.28.1.1.4.1 1 201403052200] org.netbeans.libs.findbugs [1.0 201403052200] org.netbeans.modules.findbugs [1.27 201403052200] org.apache.commons.io [1.4 1.4] org.netbeans.modules.extexecution.impl [1.20.1 201403052200] org.netbeans.modules.editor.mimelookup.impl/1 [1.26.1 201403052200] org.netbeans.modules.editor.global.format/1 [1.10.1.1 1 201403052200] org.netbeans.modules.debugger.jpda.projects [1.29.1 201403052200] org.netbeans.modules.debugger.jpda/2 [1.69.1.80 80 201403052200] org.netbeans.modules.debugger.jpda.ui/1 [1.42.1 201403052200] org.netbeans.modules.debugger.jpda.visual/1 [1.12.1 201403052200] org.netbeans.modules.debugger.jpda.js/1 [1.1.1 201403052200] org.netbeans.modules.debugger.jpda.kit [1.11.1 201403052200] org.netbeans.modules.debugger.jpda.heapwalk/1 [1.26.1 201403052200] org.netbeans.modules.css.prep [1.20.1 201403052200] org.eclipse.mylyn.bugzilla.core [3.10.0 3.10.0.20131024-1218] org.apache.commons.httpclient [3.1.0 3.1.0] org.netbeans.modules.bugzilla [1.71.1 201403052200] org.netbeans.modules.bugzilla.exceptionreporter [1.19.1 201403052200] org.netbeans.modules.bugtracking.bridge [1.40.1 201403052200] org.netbeans.modules.ant.grammar/1 [1.39.1 201403052200] org.netbeans.libs.svnClientAdapter.svnkit/1 [1.20.1 201403052200] org.netbeans.libs.svnClientAdapter.javahl/1 [1.19.1 201403052200] org.netbeans.libs.javafx [2.3.1 201403052200] org.netbeans.core.network [1.4.1 201403052200] org.netbeans.core.nativeaccess/1 [1.26.1 201403052200] org.netbeans.core.io.ui/1 [1.25.1 201403052200] org.netbeans.core.browser.webview/1 [1.10.1.1.1 1 201403052200] org.eclipse.mylyn.commons.xmlrpc [3.10.0 3.10.0.20130704-2116] org.eclipse.mylyn.commons.repositories.core [1.2.0 1.2.0.20130704-2116] org.eclipse.mylyn.commons.core [3.10.0 3.10.0.20130926-1710] org.eclipse.equinox.security [1.1.1 1.1.1.R37x_v20110822-1018] org.eclipse.equinox.registry [3.5.200 3.5.200.v20120522-1841] org.eclipse.equinox.preferences [3.4.2 3.4.2.v20120111-2020] org.eclipse.equinox.app [1.3.100 1.3.100.v20110321] org.eclipse.core.runtime.compatibility.auth [3.2.200 3.2.200.v20110110] org.eclipse.core.net [1.2.100 1.2.100.I20110511-0800] org.eclipse.core.contenttype [3.4.100 3.4.100.v20110423-0524] org.apache.xmlrpc [3.0.0 3.0.0] org.apache.ws.commons.util [1.0.1 1.0.1] org.apache.commons.lang [2.4.0 2.4.0] javaewah.dummy [0.5.6 0.5.6] com.jcraft.jzlib [1.0.7 1.0.7] INFO [org.netbeans.core.netigso.Netigso]: bundle org.eclipse.osgi@3.8.0.v20120529-1548 started INFO [org.netbeans.core.network.proxy.NetworkProxyReloader]: System network proxy resolver: Windows INFO [org.netbeans.core.network.proxy.NetworkProxyReloader]: System network proxy reloading succeeded. INFO [org.netbeans.core.network.proxy.NetworkProxyReloader]: System network proxy - mode: direct INFO [org.netbeans.core.network.proxy.NetworkProxyReloader]: System network proxy: fell to default (correct if direct mode went before) Diagnostic information Input arguments: -Dnetbeans.importclass=org.netbeans.upgrade.AutoUpgrade -Dnetbeans.accept_license_class=org.netbeans.license.AcceptLicense -client -Xss2m -Xms32m -Xmx4096m -XX:PermSize=32m -Dapple.laf.useScreenMenuBar=true -Dapple.awt.graphics.UseQuartz=true -Dsun.java2d.noddraw=true -Dsun.java2d.dpiaware=true -Dsun.zip.disableMemoryMapping=true -XX:MaxPermSize=384m -Djdk.home=C:/Developer/Java/jdk1.8.0 -Dnetbeans.home=C:\Developer\netbeans-8.0-201403052200\platform -Dnetbeans.user=C:\Users\User06\AppData\Roaming\NetBeans\8.0-201403052200 -Dnetbeans.default_userdir_root=C:\Users\User06\AppData\Roaming\NetBeans -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\User06\AppData\Roaming\NetBeans\8.0-201403052200\var\log\heapdump.hprof -Dsun.awt.keepWorkingSetOnMinimize=true -Dnetbeans.dirs=C:\Developer\netbeans-8.0-201403052200\nb;C:\Developer\netbeans-8.0-201403052200\ergonomics;C:\Developer\netbeans-8.0-201403052200\ide;C:\Developer\netbeans-8.0-201403052200\extide;C:\Developer\netbeans-8.0-201403052200\java;C:\Developer\netbeans-8.0-201403052200\apisupport;C:\Developer\netbeans-8.0-201403052200\webcommon;C:\Developer\netbeans-8.0-201403052200\websvccommon;C:\Developer\netbeans-8.0-201403052200\enterprise;C:\Developer\netbeans-8.0-201403052200\mobility;C:\Developer\netbeans-8.0-201403052200\profiler;C:\Developer\netbeans-8.0-201403052200\python;C:\Developer\netbeans-8.0-201403052200\php;C:\Developer\netbeans-8.0-201403052200\identity;C:\Developer\netbeans-8.0-201403052200\harness;C:\Developer\netbeans-8.0-201403052200\cnd;C:\Developer\netbeans-8.0-201403052200\dlight;C:\Developer\netbeans-8.0-201403052200\groovy;C:\Developer\netbeans-8.0-201403052200\extra;C:\Developer\netbeans-8.0-201403052200\javacard;C:\Developer\netbeans-8.0-201403052200\javafx exit Compiler: HotSpot 64-Bit Tiered Compilers Heap memory usage: initial 32.0MB maximum 3641.0MB Non heap memory usage: initial 2.4MB maximum -1b Garbage collector: PS Scavenge (Collections=30 Total time spent=0s) Garbage collector: PS MarkSweep (Collections=3 Total time spent=0s) Classes: loaded=8841 total loaded=8841 unloaded 0 INFO [org.netbeans.core.ui.warmup.DiagnosticTask]: Total memory 16,611,921,920 INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Resolving dependencies took: 1,702 ms INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Complete indexing of 25 binary roots took: 950 ms INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Indexing of: C:\Users\User06\Projects\NetBeans\commons-collections4-4.0-src\src\main\java took: 1,500 ms (New or modified files: 0, Deleted files: 0) [Adding listeners took: 148 ms] INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Indexing of: C:\Users\User06\Projects\NetBeans\commons-collections4-4.0-src\src\test\resources took: 242 ms (New or modified files: 0, Deleted files: 0) [Adding listeners took: 1 ms] INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Indexing of: C:\Users\User06\Projects\NetBeans\commons-collections4-4.0-src\src\main\resources took: 3 ms (New or modified files: 0, Deleted files: 0) [Adding listeners took: 0 ms] INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Indexing of: C:\Users\User06\Projects\NetBeans\commons-collections4-4.0-src\src\test\java took: 374 ms (New or modified files: 0, Deleted files: 0) [Adding listeners took: 84 ms] INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Complete indexing of 4 source roots took: 2,119 ms (New or modified files: 0, Deleted files: 0) [Adding listeners took: 233 ms] WARNING [org.netbeans.TopSecurityManager]: use of system property netbeans.home has been obsoleted in favor of InstalledFileLocator/Places at org.netbeans.Clusters.relativeDirsWithHome(Clusters.java:137) INFO [org.netbeans.modules.refactoring.spi.impl.ParametersPanel]: org.netbeans.modules.refactoring.api.AbstractRefactoring$ProgressL@7b238a21 called start multiple times INFO [org.netbeans.modules.java.hints.providers.code.CodeHintProviderImpl$WorkerImpl]: org.netbeans.modules.java.hints.suggestions.TooStrongCast.broadTypeCast java.lang.IllegalArgumentException: OTHER at org.netbeans.api.java.source.ElementHandle.createImpl(ElementHandle.java:484) at org.netbeans.api.java.source.ElementHandle.create(ElementHandle.java:397) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:208) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:201) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:141) at org.netbeans.modules.java.hints.suggestions.TooStrongCast$ReplaceTypeCast.(TooStrongCast.java:296) at org.netbeans.modules.java.hints.suggestions.TooStrongCast.broadTypeCast(TooStrongCast.java:210) Caused: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) [catch] at org.netbeans.modules.java.hints.providers.code.CodeHintProviderImpl$WorkerImpl.createErrors(CodeHintProviderImpl.java:336) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.runHint(HintsInvoker.java:790) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.doComputeHints(HintsInvoker.java:567) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHintsImpl(HintsInvoker.java:294) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:239) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:209) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:199) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:294) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:264) at org.netbeans.api.java.source.JavaSource$MultiTask.run(JavaSource.java:493) at org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask(TaskProcessor.java:593) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:191) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:163) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:206) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:203) at org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager.priorityIO(FileChangedManager.java:176) at org.netbeans.modules.masterfs.providers.ProvidedExtensions.priorityIO(ProvidedExtensions.java:360) at org.netbeans.modules.parsing.impl.Utilities.runPriorityIO(Utilities.java:74) at org.netbeans.modules.parsing.impl.TaskProcessor.runUserTask(TaskProcessor.java:203) at org.netbeans.modules.parsing.api.ParserManager.parse(ParserManager.java:108) at org.netbeans.api.java.source.JavaSource.runUserActionTaskImpl(JavaSource.java:443) at org.netbeans.api.java.source.JavaSource.runUserActionTask(JavaSource.java:414) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getLocalVerifiedSpans(BatchSearch.java:264) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.access$600(BatchSearch.java:99) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$LocalIndexEnquirer.validateResource(BatchSearch.java:574) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getVerifiedSpans(BatchSearch.java:207) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:191) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:134) at org.netbeans.modules.java.hints.spiimpl.refactoring.AbstractApplyHintsRefactoringPlugin.performApplyPattern(AbstractApplyHintsRefactoringPlugin.java:141) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.performApplyPattern(FindDuplicatesRefactoringPlugin.java:121) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.prepare(FindDuplicatesRefactoringPlugin.java:86) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare2(AbstractRefactoring.java:437) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare(AbstractRefactoring.java:421) at org.netbeans.modules.refactoring.api.AbstractRefactoring.prepare(AbstractRefactoring.java:232) at org.netbeans.modules.refactoring.spi.impl.ParametersPanel$Prepare.run(ParametersPanel.java:1026) at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1423) at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2033) SEVERE [org.openide.util.Exceptions] java.lang.IllegalArgumentException: OTHER at org.netbeans.api.java.source.ElementHandle.createImpl(ElementHandle.java:484) at org.netbeans.api.java.source.ElementHandle.create(ElementHandle.java:397) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:208) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:201) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:141) at org.netbeans.modules.java.hints.suggestions.TooStrongCast$ReplaceTypeCast.(TooStrongCast.java:296) at org.netbeans.modules.java.hints.suggestions.TooStrongCast.broadTypeCast(TooStrongCast.java:210) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) [catch] at org.netbeans.modules.java.hints.providers.code.CodeHintProviderImpl$WorkerImpl.createErrors(CodeHintProviderImpl.java:336) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.runHint(HintsInvoker.java:790) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.doComputeHints(HintsInvoker.java:567) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHintsImpl(HintsInvoker.java:294) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:239) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:209) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:199) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:294) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:264) at org.netbeans.api.java.source.JavaSource$MultiTask.run(JavaSource.java:493) at org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask(TaskProcessor.java:593) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:191) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:163) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:206) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:203) at org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager.priorityIO(FileChangedManager.java:176) at org.netbeans.modules.masterfs.providers.ProvidedExtensions.priorityIO(ProvidedExtensions.java:360) at org.netbeans.modules.parsing.impl.Utilities.runPriorityIO(Utilities.java:74) at org.netbeans.modules.parsing.impl.TaskProcessor.runUserTask(TaskProcessor.java:203) at org.netbeans.modules.parsing.api.ParserManager.parse(ParserManager.java:108) at org.netbeans.api.java.source.JavaSource.runUserActionTaskImpl(JavaSource.java:443) at org.netbeans.api.java.source.JavaSource.runUserActionTask(JavaSource.java:414) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getLocalVerifiedSpans(BatchSearch.java:264) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.access$600(BatchSearch.java:99) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$LocalIndexEnquirer.validateResource(BatchSearch.java:574) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getVerifiedSpans(BatchSearch.java:207) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:191) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:134) at org.netbeans.modules.java.hints.spiimpl.refactoring.AbstractApplyHintsRefactoringPlugin.performApplyPattern(AbstractApplyHintsRefactoringPlugin.java:141) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.performApplyPattern(FindDuplicatesRefactoringPlugin.java:121) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.prepare(FindDuplicatesRefactoringPlugin.java:86) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare2(AbstractRefactoring.java:437) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare(AbstractRefactoring.java:421) at org.netbeans.modules.refactoring.api.AbstractRefactoring.prepare(AbstractRefactoring.java:232) at org.netbeans.modules.refactoring.spi.impl.ParametersPanel$Prepare.run(ParametersPanel.java:1026) at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1423) at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2033) ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4; import org.apache.commons.collections4.bag.CollectionBag; import org.apache.commons.collections4.bag.HashBag; import org.apache.commons.collections4.bag.PredicatedBag; import org.apache.commons.collections4.bag.PredicatedSortedBag; import org.apache.commons.collections4.bag.SynchronizedBag; import org.apache.commons.collections4.bag.SynchronizedSortedBag; import org.apache.commons.collections4.bag.TransformedBag; import org.apache.commons.collections4.bag.TransformedSortedBag; import org.apache.commons.collections4.bag.TreeBag; import org.apache.commons.collections4.bag.UnmodifiableBag; import org.apache.commons.collections4.bag.UnmodifiableSortedBag; /** * Provides utility methods and decorators for {@link Bag} and {@link SortedBag} instances. * * @since 2.1 * @version $Id: BagUtils.java 1543964 2013-11-20 21:53:39Z tn $ */ public class BagUtils { /** * An empty unmodifiable bag. */ @SuppressWarnings("rawtypes") // OK, empty bag is compatible with any type public static final Bag EMPTY_BAG = UnmodifiableBag.unmodifiableBag(new HashBag()); /** * An empty unmodifiable sorted bag. */ @SuppressWarnings("rawtypes") // OK, empty bag is compatible with any type public static final Bag EMPTY_SORTED_BAG = UnmodifiableSortedBag.unmodifiableSortedBag(new TreeBag()); /** * Instantiation of BagUtils is not intended or required. */ private BagUtils() {} //----------------------------------------------------------------------- /** * Returns a synchronized (thread-safe) bag backed by the given bag. In * order to guarantee serial access, it is critical that all access to the * backing bag is accomplished through the returned bag. *

* It is imperative that the user manually synchronize on the returned bag * when iterating over it: * *

     * Bag bag = BagUtils.synchronizedBag(new HashBag());
     * ...
     * synchronized(bag) {
     *     Iterator i = bag.iterator(); // Must be in synchronized block
     *     while (i.hasNext())
     *         foo(i.next());
     *     }
     * }
     * 
* * Failure to follow this advice may result in non-deterministic behavior. * * @param the element type * @param bag the bag to synchronize, must not be null * @return a synchronized bag backed by that bag * @throws IllegalArgumentException if the Bag is null */ public static Bag synchronizedBag(final Bag bag) { return SynchronizedBag.synchronizedBag(bag); } /** * Returns an unmodifiable view of the given bag. Any modification attempts * to the returned bag will raise an {@link UnsupportedOperationException}. * * @param the element type * @param bag the bag whose unmodifiable view is to be returned, must not be null * @return an unmodifiable view of that bag * @throws IllegalArgumentException if the Bag is null */ public static Bag unmodifiableBag(final Bag bag) { return UnmodifiableBag.unmodifiableBag(bag); } /** * Returns a predicated (validating) bag backed by the given bag. *

* Only objects that pass the test in the given predicate can be added to * the bag. Trying to add an invalid object results in an * IllegalArgumentException. It is important not to use the original bag * after invoking this method, as it is a backdoor for adding invalid * objects. * * @param the element type * @param bag the bag to predicate, must not be null * @param predicate the predicate for the bag, must not be null * @return a predicated bag backed by the given bag * @throws IllegalArgumentException if the Bag or Predicate is null */ public static Bag predicatedBag(final Bag bag, final Predicate predicate) { return PredicatedBag.predicatedBag(bag, predicate); } /** * Returns a transformed bag backed by the given bag. *

* Each object is passed through the transformer as it is added to the Bag. * It is important not to use the original bag after invoking this method, * as it is a backdoor for adding untransformed objects. *

* Existing entries in the specified bag will not be transformed. * If you want that behaviour, see {@link TransformedBag#transformedBag(Bag, Transformer)}. * * @param the element type * @param bag the bag to predicate, must not be null * @param transformer the transformer for the bag, must not be null * @return a transformed bag backed by the given bag * @throws IllegalArgumentException if the Bag or Transformer is null */ public static Bag transformingBag(final Bag bag, final Transformer transformer) { return TransformedBag.transformingBag(bag, transformer); } /** * Returns a bag that complies to the Collection contract, backed by the given bag. * * @param the element type * @param bag the bag to decorate, must not be null * @return a Bag that complies to the Collection contract * @throws IllegalArgumentException if bag is null * @since 4.0 */ public static Bag collectionBag(final Bag bag) { return CollectionBag.collectionBag(bag); } //----------------------------------------------------------------------- /** * Returns a synchronized (thread-safe) sorted bag backed by the given * sorted bag. In order to guarantee serial access, it is critical that all * access to the backing bag is accomplished through the returned bag. *

* It is imperative that the user manually synchronize on the returned bag * when iterating over it: * *

     * SortedBag bag = BagUtils.synchronizedSortedBag(new TreeBag());
     * ...
     * synchronized(bag) {
     *     Iterator i = bag.iterator(); // Must be in synchronized block
     *     while (i.hasNext())
     *         foo(i.next());
     *     }
     * }
     * 
* * Failure to follow this advice may result in non-deterministic behavior. * * @param the element type * @param bag the bag to synchronize, must not be null * @return a synchronized bag backed by that bag * @throws IllegalArgumentException if the SortedBag is null */ public static SortedBag synchronizedSortedBag(final SortedBag bag) { return SynchronizedSortedBag.synchronizedSortedBag(bag); } /** * Returns an unmodifiable view of the given sorted bag. Any modification * attempts to the returned bag will raise an * {@link UnsupportedOperationException}. * * @param the element type * @param bag the bag whose unmodifiable view is to be returned, must not be null * @return an unmodifiable view of that bag * @throws IllegalArgumentException if the SortedBag is null */ public static SortedBag unmodifiableSortedBag(final SortedBag bag) { return UnmodifiableSortedBag.unmodifiableSortedBag(bag); } /** * Returns a predicated (validating) sorted bag backed by the given sorted * bag. *

* Only objects that pass the test in the given predicate can be added to * the bag. Trying to add an invalid object results in an * IllegalArgumentException. It is important not to use the original bag * after invoking this method, as it is a backdoor for adding invalid * objects. * * @param the element type * @param bag the sorted bag to predicate, must not be null * @param predicate the predicate for the bag, must not be null * @return a predicated bag backed by the given bag * @throws IllegalArgumentException if the SortedBag or Predicate is null */ public static SortedBag predicatedSortedBag(final SortedBag bag, final Predicate predicate) { return PredicatedSortedBag.predicatedSortedBag(bag, predicate); } /** * Returns a transformed sorted bag backed by the given bag. *

* Each object is passed through the transformer as it is added to the Bag. * It is important not to use the original bag after invoking this method, * as it is a backdoor for adding untransformed objects. *

* Existing entries in the specified bag will not be transformed. * If you want that behaviour, see * {@link TransformedSortedBag#transformedSortedBag(SortedBag, Transformer)}. * * @param the element type * @param bag the bag to predicate, must not be null * @param transformer the transformer for the bag, must not be null * @return a transformed bag backed by the given bag * @throws IllegalArgumentException if the Bag or Transformer is null */ public static SortedBag transformingSortedBag(final SortedBag bag, final Transformer transformer) { return TransformedSortedBag.transformingSortedBag(bag, transformer); } /** * Get an empty Bag. * * @param the element type * @return an empty Bag */ @SuppressWarnings("unchecked") // OK, empty bag is compatible with any type public static Bag emptyBag() { return (Bag) EMPTY_BAG; } /** * Get an empty SortedBag. * * @param the element type * @return an empty sorted Bag */ @SuppressWarnings("unchecked") // OK, empty bag is compatible with any type public static SortedBag emptySortedBag() { return (SortedBag) EMPTY_SORTED_BAG; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 10626; to = 10550.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4; import org.apache.commons.collections4.bag.CollectionBag; import org.apache.commons.collections4.bag.HashBag; import org.apache.commons.collections4.bag.PredicatedBag; import org.apache.commons.collections4.bag.PredicatedSortedBag; import org.apache.commons.collections4.bag.SynchronizedBag; import org.apache.commons.collections4.bag.SynchronizedSortedBag; import org.apache.commons.collections4.bag.TransformedBag; import org.apache.commons.collections4.bag.TransformedSortedBag; import org.apache.commons.collections4.bag.TreeBag; import org.apache.commons.collections4.bag.UnmodifiableBag; import org.apache.commons.collections4.bag.UnmodifiableSortedBag; /** * Provides utility methods and decorators for {@link Bag} and {@link SortedBag} instances. * * @since 2.1 * @version $Id: BagUtils.java 1543964 2013-11-20 21:53:39Z tn $ */ public class BagUtils { /** * An empty unmodifiable bag. */ @SuppressWarnings("rawtypes") // OK, empty bag is compatible with any type public static final Bag EMPTY_BAG = UnmodifiableBag.unmodifiableBag(new HashBag()); /** * An empty unmodifiable sorted bag. */ @SuppressWarnings("rawtypes") // OK, empty bag is compatible with any type public static final Bag EMPTY_SORTED_BAG = UnmodifiableSortedBag.unmodifiableSortedBag(new TreeBag()); /** * Instantiation of BagUtils is not intended or required. */ private BagUtils() {} //----------------------------------------------------------------------- /** * Returns a synchronized (thread-safe) bag backed by the given bag. In * order to guarantee serial access, it is critical that all access to the * backing bag is accomplished through the returned bag. *

* It is imperative that the user manually synchronize on the returned bag * when iterating over it: * *

     * Bag bag = BagUtils.synchronizedBag(new HashBag());
     * ...
     * synchronized(bag) {
     *     Iterator i = bag.iterator(); // Must be in synchronized block
     *     while (i.hasNext())
     *         foo(i.next());
     *     }
     * }
     * 
* * Failure to follow this advice may result in non-deterministic behavior. * * @param the element type * @param bag the bag to synchronize, must not be null * @return a synchronized bag backed by that bag * @throws IllegalArgumentException if the Bag is null */ public static Bag synchronizedBag(final Bag bag) { return SynchronizedBag.synchronizedBag(bag); } /** * Returns an unmodifiable view of the given bag. Any modification attempts * to the returned bag will raise an {@link UnsupportedOperationException}. * * @param the element type * @param bag the bag whose unmodifiable view is to be returned, must not be null * @return an unmodifiable view of that bag * @throws IllegalArgumentException if the Bag is null */ public static Bag unmodifiableBag(final Bag bag) { return UnmodifiableBag.unmodifiableBag(bag); } /** * Returns a predicated (validating) bag backed by the given bag. *

* Only objects that pass the test in the given predicate can be added to * the bag. Trying to add an invalid object results in an * IllegalArgumentException. It is important not to use the original bag * after invoking this method, as it is a backdoor for adding invalid * objects. * * @param the element type * @param bag the bag to predicate, must not be null * @param predicate the predicate for the bag, must not be null * @return a predicated bag backed by the given bag * @throws IllegalArgumentException if the Bag or Predicate is null */ public static Bag predicatedBag(final Bag bag, final Predicate predicate) { return PredicatedBag.predicatedBag(bag, predicate); } /** * Returns a transformed bag backed by the given bag. *

* Each object is passed through the transformer as it is added to the Bag. * It is important not to use the original bag after invoking this method, * as it is a backdoor for adding untransformed objects. *

* Existing entries in the specified bag will not be transformed. * If you want that behaviour, see {@link TransformedBag#transformedBag(Bag, Transformer)}. * * @param the element type * @param bag the bag to predicate, must not be null * @param transformer the transformer for the bag, must not be null * @return a transformed bag backed by the given bag * @throws IllegalArgumentException if the Bag or Transformer is null */ public static Bag transformingBag(final Bag bag, final Transformer transformer) { return TransformedBag.transformingBag(bag, transformer); } /** * Returns a bag that complies to the Collection contract, backed by the given bag. * * @param the element type * @param bag the bag to decorate, must not be null * @return a Bag that complies to the Collection contract * @throws IllegalArgumentException if bag is null * @since 4.0 */ public static Bag collectionBag(final Bag bag) { return CollectionBag.collectionBag(bag); } //----------------------------------------------------------------------- /** * Returns a synchronized (thread-safe) sorted bag backed by the given * sorted bag. In order to guarantee serial access, it is critical that all * access to the backing bag is accomplished through the returned bag. *

* It is imperative that the user manually synchronize on the returned bag * when iterating over it: * *

     * SortedBag bag = BagUtils.synchronizedSortedBag(new TreeBag());
     * ...
     * synchronized(bag) {
     *     Iterator i = bag.iterator(); // Must be in synchronized block
     *     while (i.hasNext())
     *         foo(i.next());
     *     }
     * }
     * 
* * Failure to follow this advice may result in non-deterministic behavior. * * @param the element type * @param bag the bag to synchronize, must not be null * @return a synchronized bag backed by that bag * @throws IllegalArgumentException if the SortedBag is null */ public static SortedBag synchronizedSortedBag(final SortedBag bag) { return SynchronizedSortedBag.synchronizedSortedBag(bag); } /** * Returns an unmodifiable view of the given sorted bag. Any modification * attempts to the returned bag will raise an * {@link UnsupportedOperationException}. * * @param the element type * @param bag the bag whose unmodifiable view is to be returned, must not be null * @return an unmodifiable view of that bag * @throws IllegalArgumentException if the SortedBag is null */ public static SortedBag unmodifiableSortedBag(final SortedBag bag) { return UnmodifiableSortedBag.unmodifiableSortedBag(bag); } /** * Returns a predicated (validating) sorted bag backed by the given sorted * bag. *

* Only objects that pass the test in the given predicate can be added to * the bag. Trying to add an invalid object results in an * IllegalArgumentException. It is important not to use the original bag * after invoking this method, as it is a backdoor for adding invalid * objects. * * @param the element type * @param bag the sorted bag to predicate, must not be null * @param predicate the predicate for the bag, must not be null * @return a predicated bag backed by the given bag * @throws IllegalArgumentException if the SortedBag or Predicate is null */ public static SortedBag predicatedSortedBag(final SortedBag bag, final Predicate predicate) { return PredicatedSortedBag.predicatedSortedBag(bag, predicate); } /** * Returns a transformed sorted bag backed by the given bag. *

* Each object is passed through the transformer as it is added to the Bag. * It is important not to use the original bag after invoking this method, * as it is a backdoor for adding untransformed objects. *

* Existing entries in the specified bag will not be transformed. * If you want that behaviour, see * {@link TransformedSortedBag#transformedSortedBag(SortedBag, Transformer)}. * * @param the element type * @param bag the bag to predicate, must not be null * @param transformer the transformer for the bag, must not be null * @return a transformed bag backed by the given bag * @throws IllegalArgumentException if the Bag or Transformer is null */ public static SortedBag transformingSortedBag(final SortedBag bag, final Transformer transformer) { return TransformedSortedBag.transformingSortedBag(bag, transformer); } /** * Get an empty Bag. * * @param the element type * @return an empty Bag */ @SuppressWarnings("unchecked") // OK, empty bag is compatible with any type public static Bag emptyBag() { return (Bag) EMPTY_BAG; } /** * Get an empty SortedBag. * * @param the element type * @return an empty sorted Bag */ @SuppressWarnings("unchecked") // OK, empty bag is compatible with any type public static SortedBag emptySortedBag() { return (SortedBag) EMPTY_SORTED_BAG; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 10626; to = 10550.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.splitmap; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Map; import org.apache.commons.collections4.Put; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.LinkedMap; /** * Decorates another {@link Map} to transform objects that are added. *

* The Map put methods and Map.Entry setValue method are affected by this class. * Thus objects must be removed or searched for using their transformed form. * For example, if the transformation converts Strings to Integers, you must use * the Integer form to remove objects. *

* Note that TransformedMap is not synchronized and is not * thread-safe. If you wish to use this map from multiple threads * concurrently, you must use appropriate synchronization. The simplest approach * is to wrap this map using {@link java.util.Collections#synchronizedMap(Map)}. * This class may throw exceptions when accessed by concurrent threads without * synchronization. *

* The "put" and "get" type constraints of this class are mutually independent; * contrast with {@link org.apache.commons.collections4.map.TransformedMap} which, * by virtue of its implementing {@link Map}<K, V>, must be constructed in such * a way that its read and write parameters are generalized to a common (super-)type. * In practice this would often mean >Object, Object>, defeating * much of the usefulness of having parameterized types. *

* On the downside, this class is not drop-in compatible with {@link java.util.Map} * but is intended to be worked with either directly or by {@link Put} and * {@link org.apache.commons.collections4.Get Get} generalizations. * * @since 4.0 * @version $Id: TransformedSplitMap.java 1491944 2013-06-11 20:29:22Z tn $ * * @see org.apache.commons.collections4.SplitMapUtils#readableMap(Get) * @see org.apache.commons.collections4.SplitMapUtils#writableMap(Put) */ public class TransformedSplitMap extends AbstractIterableGetMapDecorator implements Put, Serializable { /** Serialization version */ private static final long serialVersionUID = 5966875321133456994L; /** The transformer to use for the key */ private final Transformer keyTransformer; /** The transformer to use for the value */ private final Transformer valueTransformer; /** * Factory method to create a transforming map. *

* If there are any elements already in the map being decorated, they are * NOT transformed. * * @param the input key type * @param the output key type * @param the input value type * @param the output value type * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null * means no transformation * @param valueTransformer the transformer to use for value conversion, null * means no transformation * @return a new transformed map * @throws IllegalArgumentException if map is null */ public static TransformedSplitMap transformingMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { return new TransformedSplitMap(map, keyTransformer, valueTransformer); } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). *

* If there are any elements already in the collection being decorated, they * are NOT transformed. * * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null * means no conversion * @param valueTransformer the transformer to use for value conversion, null * means no conversion * @throws IllegalArgumentException if map is null */ protected TransformedSplitMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { super(map); if (keyTransformer == null) { throw new IllegalArgumentException("keyTransformer cannot be null"); } this.keyTransformer = keyTransformer; if (valueTransformer == null) { throw new IllegalArgumentException("valueTransformer cannot be null"); } this.valueTransformer = valueTransformer; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(decorated()); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 3.1 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Transforms a key. *

* The transformer itself may throw an exception if necessary. * * @param object the object to transform * @return the transformed object */ protected K transformKey(final J object) { return keyTransformer.transform(object); } /** * Transforms a value. *

* The transformer itself may throw an exception if necessary. * * @param object the object to transform * @return the transformed object */ protected V transformValue(final U object) { return valueTransformer.transform(object); } /** * Transforms a map. *

* The transformer itself may throw an exception if necessary. * * @param map the map to transform * @return the transformed object */ @SuppressWarnings("unchecked") protected Map transformMap(final Map map) { if (map.isEmpty()) { return (Map) map; } final Map result = new LinkedMap(map.size()); for (final Map.Entry entry : map.entrySet()) { result.put(transformKey(entry.getKey()), transformValue(entry.getValue())); } return result; } /** * Override to transform the value when using setValue. * * @param value the value to transform * @return the transformed value */ protected V checkSetValue(final U value) { return valueTransformer.transform(value); } //----------------------------------------------------------------------- public V put(final J key, final U value) { return decorated().put(transformKey(key), transformValue(value)); } public void putAll(final Map mapToCopy) { decorated().putAll(transformMap(mapToCopy)); } public void clear() { decorated().clear(); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 6229; to = 6145.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.splitmap; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Map; import org.apache.commons.collections4.Put; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.LinkedMap; /** * Decorates another {@link Map} to transform objects that are added. *

* The Map put methods and Map.Entry setValue method are affected by this class. * Thus objects must be removed or searched for using their transformed form. * For example, if the transformation converts Strings to Integers, you must use * the Integer form to remove objects. *

* Note that TransformedMap is not synchronized and is not * thread-safe. If you wish to use this map from multiple threads * concurrently, you must use appropriate synchronization. The simplest approach * is to wrap this map using {@link java.util.Collections#synchronizedMap(Map)}. * This class may throw exceptions when accessed by concurrent threads without * synchronization. *

* The "put" and "get" type constraints of this class are mutually independent; * contrast with {@link org.apache.commons.collections4.map.TransformedMap} which, * by virtue of its implementing {@link Map}<K, V>, must be constructed in such * a way that its read and write parameters are generalized to a common (super-)type. * In practice this would often mean >Object, Object>, defeating * much of the usefulness of having parameterized types. *

* On the downside, this class is not drop-in compatible with {@link java.util.Map} * but is intended to be worked with either directly or by {@link Put} and * {@link org.apache.commons.collections4.Get Get} generalizations. * * @since 4.0 * @version $Id: TransformedSplitMap.java 1491944 2013-06-11 20:29:22Z tn $ * * @see org.apache.commons.collections4.SplitMapUtils#readableMap(Get) * @see org.apache.commons.collections4.SplitMapUtils#writableMap(Put) */ public class TransformedSplitMap extends AbstractIterableGetMapDecorator implements Put, Serializable { /** Serialization version */ private static final long serialVersionUID = 5966875321133456994L; /** The transformer to use for the key */ private final Transformer keyTransformer; /** The transformer to use for the value */ private final Transformer valueTransformer; /** * Factory method to create a transforming map. *

* If there are any elements already in the map being decorated, they are * NOT transformed. * * @param the input key type * @param the output key type * @param the input value type * @param the output value type * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null * means no transformation * @param valueTransformer the transformer to use for value conversion, null * means no transformation * @return a new transformed map * @throws IllegalArgumentException if map is null */ public static TransformedSplitMap transformingMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { return new TransformedSplitMap(map, keyTransformer, valueTransformer); } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). *

* If there are any elements already in the collection being decorated, they * are NOT transformed. * * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null * means no conversion * @param valueTransformer the transformer to use for value conversion, null * means no conversion * @throws IllegalArgumentException if map is null */ protected TransformedSplitMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { super(map); if (keyTransformer == null) { throw new IllegalArgumentException("keyTransformer cannot be null"); } this.keyTransformer = keyTransformer; if (valueTransformer == null) { throw new IllegalArgumentException("valueTransformer cannot be null"); } this.valueTransformer = valueTransformer; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(decorated()); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 3.1 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Transforms a key. *

* The transformer itself may throw an exception if necessary. * * @param object the object to transform * @return the transformed object */ protected K transformKey(final J object) { return keyTransformer.transform(object); } /** * Transforms a value. *

* The transformer itself may throw an exception if necessary. * * @param object the object to transform * @return the transformed object */ protected V transformValue(final U object) { return valueTransformer.transform(object); } /** * Transforms a map. *

* The transformer itself may throw an exception if necessary. * * @param map the map to transform * @return the transformed object */ @SuppressWarnings("unchecked") protected Map transformMap(final Map map) { if (map.isEmpty()) { return (Map) map; } final Map result = new LinkedMap(map.size()); for (final Map.Entry entry : map.entrySet()) { result.put(transformKey(entry.getKey()), transformValue(entry.getValue())); } return result; } /** * Override to transform the value when using setValue. * * @param value the value to transform * @return the transformed value */ protected V checkSetValue(final U value) { return valueTransformer.transform(value); } //----------------------------------------------------------------------- public V put(final J key, final U value) { return decorated().put(transformKey(key), transformValue(value)); } public void putAll(final Map mapToCopy) { decorated().putAll(transformMap(mapToCopy)); } public void clear() { decorated().clear(); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 6229; to = 6145.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.functors; import java.io.Serializable; import org.apache.commons.collections4.Factory; /** * Factory implementation that returns the same constant each time. *

* No check is made that the object is immutable. In general, only immutable * objects should use the constant factory. Mutable objects should * use the prototype factory. * * @since 3.0 * @version $Id: ConstantFactory.java 1543950 2013-11-20 21:13:35Z tn $ */ public class ConstantFactory implements Factory, Serializable { /** Serial version UID */ private static final long serialVersionUID = -3520677225766901240L; /** Returns null each time */ @SuppressWarnings("rawtypes") // The null factory works for all object types public static final Factory NULL_INSTANCE = new ConstantFactory(null); /** The closures to call in turn */ private final T iConstant; /** * Factory method that performs validation. * * @param the type of the constant * @param constantToReturn the constant object to return each time in the factory * @return the constant factory. */ @SuppressWarnings("unchecked") // The null factory works for all object types public static Factory constantFactory(final T constantToReturn) { if (constantToReturn == null) { return (Factory) NULL_INSTANCE; } return new ConstantFactory(constantToReturn); } /** * Constructor that performs no validation. * Use constantFactory if you want that. * * @param constantToReturn the constant to return each time */ public ConstantFactory(final T constantToReturn) { super(); iConstant = constantToReturn; } /** * Always return constant. * * @return the stored constant value */ public T create() { return iConstant; } /** * Gets the constant. * * @return the constant * @since 3.1 */ public T getConstant() { return iConstant; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 2058; to = 1980.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.functors; import java.io.Serializable; import org.apache.commons.collections4.Factory; /** * Factory implementation that returns the same constant each time. *

* No check is made that the object is immutable. In general, only immutable * objects should use the constant factory. Mutable objects should * use the prototype factory. * * @since 3.0 * @version $Id: ConstantFactory.java 1543950 2013-11-20 21:13:35Z tn $ */ public class ConstantFactory implements Factory, Serializable { /** Serial version UID */ private static final long serialVersionUID = -3520677225766901240L; /** Returns null each time */ @SuppressWarnings("rawtypes") // The null factory works for all object types public static final Factory NULL_INSTANCE = new ConstantFactory(null); /** The closures to call in turn */ private final T iConstant; /** * Factory method that performs validation. * * @param the type of the constant * @param constantToReturn the constant object to return each time in the factory * @return the constant factory. */ @SuppressWarnings("unchecked") // The null factory works for all object types public static Factory constantFactory(final T constantToReturn) { if (constantToReturn == null) { return (Factory) NULL_INSTANCE; } return new ConstantFactory(constantToReturn); } /** * Constructor that performs no validation. * Use constantFactory if you want that. * * @param constantToReturn the constant to return each time */ public ConstantFactory(final T constantToReturn) { super(); iConstant = constantToReturn; } /** * Always return constant. * * @return the stored constant value */ public T create() { return iConstant; } /** * Gets the constant. * * @return the constant * @since 3.1 */ public T getConstant() { return iConstant; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 2058; to = 1980.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.functors; import java.io.Serializable; import org.apache.commons.collections4.Transformer; /** * Transformer implementation that returns the same constant each time. *

* No check is made that the object is immutable. In general, only immutable * objects should use the constant factory. Mutable objects should * use the prototype factory. * * @since 3.0 * @version $Id: ConstantTransformer.java 1543950 2013-11-20 21:13:35Z tn $ */ public class ConstantTransformer implements Transformer, Serializable { /** Serial version UID */ private static final long serialVersionUID = 6374440726369055124L; /** Returns null each time */ @SuppressWarnings("rawtypes") public static final Transformer NULL_INSTANCE = new ConstantTransformer(null); /** The closures to call in turn */ private final O iConstant; /** * Get a typed null instance. * * @param the input type * @param the output type * @return Transformer that always returns null. */ @SuppressWarnings("unchecked") // The null transformer works for all object types public static Transformer nullTransformer() { return (Transformer) NULL_INSTANCE; } /** * Transformer method that performs validation. * * @param the input type * @param the output type * @param constantToReturn the constant object to return each time in the factory * @return the constant factory. */ public static Transformer constantTransformer(final O constantToReturn) { if (constantToReturn == null) { return nullTransformer(); } return new ConstantTransformer(constantToReturn); } /** * Constructor that performs no validation. * Use constantTransformer if you want that. * * @param constantToReturn the constant to return each time */ public ConstantTransformer(final O constantToReturn) { super(); iConstant = constantToReturn; } /** * Transforms the input by ignoring it and returning the stored constant instead. * * @param input the input object which is ignored * @return the stored constant */ public O transform(final I input) { return iConstant; } /** * Gets the constant. * * @return the constant * @since 3.1 */ public O getConstant() { return iConstant; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (obj instanceof ConstantTransformer == false) { return false; } final Object otherConstant = ((ConstantTransformer) obj).getConstant(); return otherConstant == getConstant() || otherConstant != null && otherConstant.equals(getConstant()); } /** * {@inheritDoc} */ @Override public int hashCode() { int result = "ConstantTransformer".hashCode() << 2; if (getConstant() != null) { result |= getConstant().hashCode(); } return result; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 1989; to = 1907.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.functors; import java.io.Serializable; import org.apache.commons.collections4.Transformer; /** * Transformer implementation that returns the same constant each time. *

* No check is made that the object is immutable. In general, only immutable * objects should use the constant factory. Mutable objects should * use the prototype factory. * * @since 3.0 * @version $Id: ConstantTransformer.java 1543950 2013-11-20 21:13:35Z tn $ */ public class ConstantTransformer implements Transformer, Serializable { /** Serial version UID */ private static final long serialVersionUID = 6374440726369055124L; /** Returns null each time */ @SuppressWarnings("rawtypes") public static final Transformer NULL_INSTANCE = new ConstantTransformer(null); /** The closures to call in turn */ private final O iConstant; /** * Get a typed null instance. * * @param the input type * @param the output type * @return Transformer that always returns null. */ @SuppressWarnings("unchecked") // The null transformer works for all object types public static Transformer nullTransformer() { return (Transformer) NULL_INSTANCE; } /** * Transformer method that performs validation. * * @param the input type * @param the output type * @param constantToReturn the constant object to return each time in the factory * @return the constant factory. */ public static Transformer constantTransformer(final O constantToReturn) { if (constantToReturn == null) { return nullTransformer(); } return new ConstantTransformer(constantToReturn); } /** * Constructor that performs no validation. * Use constantTransformer if you want that. * * @param constantToReturn the constant to return each time */ public ConstantTransformer(final O constantToReturn) { super(); iConstant = constantToReturn; } /** * Transforms the input by ignoring it and returning the stored constant instead. * * @param input the input object which is ignored * @return the stored constant */ public O transform(final I input) { return iConstant; } /** * Gets the constant. * * @return the constant * @since 3.1 */ public O getConstant() { return iConstant; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (obj instanceof ConstantTransformer == false) { return false; } final Object otherConstant = ((ConstantTransformer) obj).getConstant(); return otherConstant == getConstant() || otherConstant != null && otherConstant.equals(getConstant()); } /** * {@inheritDoc} */ @Override public int hashCode() { int result = "ConstantTransformer".hashCode() << 2; if (getConstant() != null) { result |= getConstant().hashCode(); } return result; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 1989; to = 1907.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.collection; import java.util.Collection; import java.util.HashMap; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.MultiValueMap; /** * An IndexedCollection is a Map-like view onto a Collection. It accepts a * keyTransformer to define how the keys are converted from the values. *

* Modifications made to this decorator modify the index as well as the * decorated {@link Collection}. However, modifications to the underlying * {@link Collection} will not update the index and it will get out of sync. *

* If modification of the decorated {@link Collection} is unavoidable, then a * call to {@link #reindex()} will update the index to the current contents of * the {@link Collection}. * * @param the type of object in the index. * @param the type of object in the collection. * * @since 4.0 * @version $Id: IndexedCollection.java 1543740 2013-11-20 09:33:14Z ebourg $ */ public class IndexedCollection extends AbstractCollectionDecorator { /** Serialization version */ private static final long serialVersionUID = -5512610452568370038L; /** The {@link Transformer} for generating index keys. */ private final Transformer keyTransformer; /** The map of indexes to collected objects. */ private final MultiMap index; /** The uniqueness constraint for the index. */ private final boolean uniqueIndex; /** * Create an {@link IndexedCollection} for a unique index. *

* If an element is added, which maps to an existing key, an {@link IllegalArgumentException} * will be thrown. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection uniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), true); } /** * Create an {@link IndexedCollection} for a non-unique index. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection nonUniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), false); } /** * Create a {@link IndexedCollection}. * * @param coll decorated {@link Collection} * @param keyTransformer {@link Transformer} for generating index keys * @param map map to use as index * @param uniqueIndex if the index shall enforce uniqueness of index keys */ public IndexedCollection(final Collection coll, final Transformer keyTransformer, final MultiMap map, final boolean uniqueIndex) { super(coll); this.keyTransformer = keyTransformer; this.index = map; this.uniqueIndex = uniqueIndex; reindex(); } /** * {@inheritDoc} * * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ @Override public boolean add(final C object) { final boolean added = super.add(object); if (added) { addToIndex(object); } return added; } @Override public boolean addAll(final Collection coll) { boolean changed = false; for (final C c: coll) { changed |= add(c); } return changed; } @Override public void clear() { super.clear(); index.clear(); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @SuppressWarnings("unchecked") @Override public boolean contains(final Object object) { return index.containsKey(keyTransformer.transform((C) object)); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @Override public boolean containsAll(final Collection coll) { for (final Object o : coll) { if (!contains(o)) { return false; } } return true; } /** * Get the element associated with the given key. *

* In case of a non-unique index, this method will return the first * value associated with the given key. To retrieve all elements associated * with a key, use {@link #values(Object)}. * * @param key key to look up * @return element found * @see #values(Object) */ public C get(final K key) { @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection final Collection coll = (Collection) index.get(key); return coll == null ? null : coll.iterator().next(); } /** * Get all elements associated with the given key. * * @param key key to look up * @return a collection of elements found, or null if {@code contains(key) == false} */ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection public Collection values(final K key) { return (Collection) index.get(key); } /** * Clears the index and re-indexes the entire decorated {@link Collection}. */ public void reindex() { index.clear(); for (final C c : decorated()) { addToIndex(c); } } @SuppressWarnings("unchecked") @Override public boolean remove(final Object object) { final boolean removed = super.remove(object); if (removed) { removeFromIndex((C) object); } return removed; } @Override public boolean removeAll(final Collection coll) { boolean changed = false; for (final Object o : coll) { changed |= remove(o); } return changed; } @Override public boolean retainAll(final Collection coll) { final boolean changed = super.retainAll(coll); if (changed) { reindex(); } return changed; } //----------------------------------------------------------------------- /** * Provides checking for adding the index. * * @param object the object to index * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ private void addToIndex(final C object) { final K key = keyTransformer.transform(object); if (uniqueIndex && index.containsKey(key)) { throw new IllegalArgumentException("Duplicate key in uniquely indexed collection."); } index.put(key, object); } /** * Removes an object from the index. * * @param object the object to remove */ private void removeFromIndex(final C object) { index.remove(keyTransformer.transform(object)); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 5098; to = 5094.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.collection; import java.util.Collection; import java.util.HashMap; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.MultiValueMap; /** * An IndexedCollection is a Map-like view onto a Collection. It accepts a * keyTransformer to define how the keys are converted from the values. *

* Modifications made to this decorator modify the index as well as the * decorated {@link Collection}. However, modifications to the underlying * {@link Collection} will not update the index and it will get out of sync. *

* If modification of the decorated {@link Collection} is unavoidable, then a * call to {@link #reindex()} will update the index to the current contents of * the {@link Collection}. * * @param the type of object in the index. * @param the type of object in the collection. * * @since 4.0 * @version $Id: IndexedCollection.java 1543740 2013-11-20 09:33:14Z ebourg $ */ public class IndexedCollection extends AbstractCollectionDecorator { /** Serialization version */ private static final long serialVersionUID = -5512610452568370038L; /** The {@link Transformer} for generating index keys. */ private final Transformer keyTransformer; /** The map of indexes to collected objects. */ private final MultiMap index; /** The uniqueness constraint for the index. */ private final boolean uniqueIndex; /** * Create an {@link IndexedCollection} for a unique index. *

* If an element is added, which maps to an existing key, an {@link IllegalArgumentException} * will be thrown. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection uniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), true); } /** * Create an {@link IndexedCollection} for a non-unique index. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection nonUniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), false); } /** * Create a {@link IndexedCollection}. * * @param coll decorated {@link Collection} * @param keyTransformer {@link Transformer} for generating index keys * @param map map to use as index * @param uniqueIndex if the index shall enforce uniqueness of index keys */ public IndexedCollection(final Collection coll, final Transformer keyTransformer, final MultiMap map, final boolean uniqueIndex) { super(coll); this.keyTransformer = keyTransformer; this.index = map; this.uniqueIndex = uniqueIndex; reindex(); } /** * {@inheritDoc} * * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ @Override public boolean add(final C object) { final boolean added = super.add(object); if (added) { addToIndex(object); } return added; } @Override public boolean addAll(final Collection coll) { boolean changed = false; for (final C c: coll) { changed |= add(c); } return changed; } @Override public void clear() { super.clear(); index.clear(); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @SuppressWarnings("unchecked") @Override public boolean contains(final Object object) { return index.containsKey(keyTransformer.transform((C) object)); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @Override public boolean containsAll(final Collection coll) { for (final Object o : coll) { if (!contains(o)) { return false; } } return true; } /** * Get the element associated with the given key. *

* In case of a non-unique index, this method will return the first * value associated with the given key. To retrieve all elements associated * with a key, use {@link #values(Object)}. * * @param key key to look up * @return element found * @see #values(Object) */ public C get(final K key) { @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection final Collection coll = (Collection) index.get(key); return coll == null ? null : coll.iterator().next(); } /** * Get all elements associated with the given key. * * @param key key to look up * @return a collection of elements found, or null if {@code contains(key) == false} */ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection public Collection values(final K key) { return (Collection) index.get(key); } /** * Clears the index and re-indexes the entire decorated {@link Collection}. */ public void reindex() { index.clear(); for (final C c : decorated()) { addToIndex(c); } } @SuppressWarnings("unchecked") @Override public boolean remove(final Object object) { final boolean removed = super.remove(object); if (removed) { removeFromIndex((C) object); } return removed; } @Override public boolean removeAll(final Collection coll) { boolean changed = false; for (final Object o : coll) { changed |= remove(o); } return changed; } @Override public boolean retainAll(final Collection coll) { final boolean changed = super.retainAll(coll); if (changed) { reindex(); } return changed; } //----------------------------------------------------------------------- /** * Provides checking for adding the index. * * @param object the object to index * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ private void addToIndex(final C object) { final K key = keyTransformer.transform(object); if (uniqueIndex && index.containsKey(key)) { throw new IllegalArgumentException("Duplicate key in uniquely indexed collection."); } index.put(key, object); } /** * Removes an object from the index. * * @param object the object to remove */ private void removeFromIndex(final C object) { index.remove(keyTransformer.transform(object)); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 5769; to = 5765.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.collection; import java.util.Collection; import java.util.HashMap; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.MultiValueMap; /** * An IndexedCollection is a Map-like view onto a Collection. It accepts a * keyTransformer to define how the keys are converted from the values. *

* Modifications made to this decorator modify the index as well as the * decorated {@link Collection}. However, modifications to the underlying * {@link Collection} will not update the index and it will get out of sync. *

* If modification of the decorated {@link Collection} is unavoidable, then a * call to {@link #reindex()} will update the index to the current contents of * the {@link Collection}. * * @param the type of object in the index. * @param the type of object in the collection. * * @since 4.0 * @version $Id: IndexedCollection.java 1543740 2013-11-20 09:33:14Z ebourg $ */ public class IndexedCollection extends AbstractCollectionDecorator { /** Serialization version */ private static final long serialVersionUID = -5512610452568370038L; /** The {@link Transformer} for generating index keys. */ private final Transformer keyTransformer; /** The map of indexes to collected objects. */ private final MultiMap index; /** The uniqueness constraint for the index. */ private final boolean uniqueIndex; /** * Create an {@link IndexedCollection} for a unique index. *

* If an element is added, which maps to an existing key, an {@link IllegalArgumentException} * will be thrown. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection uniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), true); } /** * Create an {@link IndexedCollection} for a non-unique index. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection nonUniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), false); } /** * Create a {@link IndexedCollection}. * * @param coll decorated {@link Collection} * @param keyTransformer {@link Transformer} for generating index keys * @param map map to use as index * @param uniqueIndex if the index shall enforce uniqueness of index keys */ public IndexedCollection(final Collection coll, final Transformer keyTransformer, final MultiMap map, final boolean uniqueIndex) { super(coll); this.keyTransformer = keyTransformer; this.index = map; this.uniqueIndex = uniqueIndex; reindex(); } /** * {@inheritDoc} * * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ @Override public boolean add(final C object) { final boolean added = super.add(object); if (added) { addToIndex(object); } return added; } @Override public boolean addAll(final Collection coll) { boolean changed = false; for (final C c: coll) { changed |= add(c); } return changed; } @Override public void clear() { super.clear(); index.clear(); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @SuppressWarnings("unchecked") @Override public boolean contains(final Object object) { return index.containsKey(keyTransformer.transform((C) object)); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @Override public boolean containsAll(final Collection coll) { for (final Object o : coll) { if (!contains(o)) { return false; } } return true; } /** * Get the element associated with the given key. *

* In case of a non-unique index, this method will return the first * value associated with the given key. To retrieve all elements associated * with a key, use {@link #values(Object)}. * * @param key key to look up * @return element found * @see #values(Object) */ public C get(final K key) { @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection final Collection coll = (Collection) index.get(key); return coll == null ? null : coll.iterator().next(); } /** * Get all elements associated with the given key. * * @param key key to look up * @return a collection of elements found, or null if {@code contains(key) == false} */ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection public Collection values(final K key) { return (Collection) index.get(key); } /** * Clears the index and re-indexes the entire decorated {@link Collection}. */ public void reindex() { index.clear(); for (final C c : decorated()) { addToIndex(c); } } @SuppressWarnings("unchecked") @Override public boolean remove(final Object object) { final boolean removed = super.remove(object); if (removed) { removeFromIndex((C) object); } return removed; } @Override public boolean removeAll(final Collection coll) { boolean changed = false; for (final Object o : coll) { changed |= remove(o); } return changed; } @Override public boolean retainAll(final Collection coll) { final boolean changed = super.retainAll(coll); if (changed) { reindex(); } return changed; } //----------------------------------------------------------------------- /** * Provides checking for adding the index. * * @param object the object to index * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ private void addToIndex(final C object) { final K key = keyTransformer.transform(object); if (uniqueIndex && index.containsKey(key)) { throw new IllegalArgumentException("Duplicate key in uniquely indexed collection."); } index.put(key, object); } /** * Removes an object from the index. * * @param object the object to remove */ private void removeFromIndex(final C object) { index.remove(keyTransformer.transform(object)); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 6911; to = 6826.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.collection; import java.util.Collection; import java.util.HashMap; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.MultiValueMap; /** * An IndexedCollection is a Map-like view onto a Collection. It accepts a * keyTransformer to define how the keys are converted from the values. *

* Modifications made to this decorator modify the index as well as the * decorated {@link Collection}. However, modifications to the underlying * {@link Collection} will not update the index and it will get out of sync. *

* If modification of the decorated {@link Collection} is unavoidable, then a * call to {@link #reindex()} will update the index to the current contents of * the {@link Collection}. * * @param the type of object in the index. * @param the type of object in the collection. * * @since 4.0 * @version $Id: IndexedCollection.java 1543740 2013-11-20 09:33:14Z ebourg $ */ public class IndexedCollection extends AbstractCollectionDecorator { /** Serialization version */ private static final long serialVersionUID = -5512610452568370038L; /** The {@link Transformer} for generating index keys. */ private final Transformer keyTransformer; /** The map of indexes to collected objects. */ private final MultiMap index; /** The uniqueness constraint for the index. */ private final boolean uniqueIndex; /** * Create an {@link IndexedCollection} for a unique index. *

* If an element is added, which maps to an existing key, an {@link IllegalArgumentException} * will be thrown. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection uniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), true); } /** * Create an {@link IndexedCollection} for a non-unique index. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection nonUniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), false); } /** * Create a {@link IndexedCollection}. * * @param coll decorated {@link Collection} * @param keyTransformer {@link Transformer} for generating index keys * @param map map to use as index * @param uniqueIndex if the index shall enforce uniqueness of index keys */ public IndexedCollection(final Collection coll, final Transformer keyTransformer, final MultiMap map, final boolean uniqueIndex) { super(coll); this.keyTransformer = keyTransformer; this.index = map; this.uniqueIndex = uniqueIndex; reindex(); } /** * {@inheritDoc} * * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ @Override public boolean add(final C object) { final boolean added = super.add(object); if (added) { addToIndex(object); } return added; } @Override public boolean addAll(final Collection coll) { boolean changed = false; for (final C c: coll) { changed |= add(c); } return changed; } @Override public void clear() { super.clear(); index.clear(); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @SuppressWarnings("unchecked") @Override public boolean contains(final Object object) { return index.containsKey(keyTransformer.transform((C) object)); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @Override public boolean containsAll(final Collection coll) { for (final Object o : coll) { if (!contains(o)) { return false; } } return true; } /** * Get the element associated with the given key. *

* In case of a non-unique index, this method will return the first * value associated with the given key. To retrieve all elements associated * with a key, use {@link #values(Object)}. * * @param key key to look up * @return element found * @see #values(Object) */ public C get(final K key) { @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection final Collection coll = (Collection) index.get(key); return coll == null ? null : coll.iterator().next(); } /** * Get all elements associated with the given key. * * @param key key to look up * @return a collection of elements found, or null if {@code contains(key) == false} */ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection public Collection values(final K key) { return (Collection) index.get(key); } /** * Clears the index and re-indexes the entire decorated {@link Collection}. */ public void reindex() { index.clear(); for (final C c : decorated()) { addToIndex(c); } } @SuppressWarnings("unchecked") @Override public boolean remove(final Object object) { final boolean removed = super.remove(object); if (removed) { removeFromIndex((C) object); } return removed; } @Override public boolean removeAll(final Collection coll) { boolean changed = false; for (final Object o : coll) { changed |= remove(o); } return changed; } @Override public boolean retainAll(final Collection coll) { final boolean changed = super.retainAll(coll); if (changed) { reindex(); } return changed; } //----------------------------------------------------------------------- /** * Provides checking for adding the index. * * @param object the object to index * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ private void addToIndex(final C object) { final K key = keyTransformer.transform(object); if (uniqueIndex && index.containsKey(key)) { throw new IllegalArgumentException("Duplicate key in uniquely indexed collection."); } index.put(key, object); } /** * Removes an object from the index. * * @param object the object to remove */ private void removeFromIndex(final C object) { index.remove(keyTransformer.transform(object)); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 7504; to = 7500.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.collection; import java.util.Collection; import java.util.HashMap; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.MultiValueMap; /** * An IndexedCollection is a Map-like view onto a Collection. It accepts a * keyTransformer to define how the keys are converted from the values. *

* Modifications made to this decorator modify the index as well as the * decorated {@link Collection}. However, modifications to the underlying * {@link Collection} will not update the index and it will get out of sync. *

* If modification of the decorated {@link Collection} is unavoidable, then a * call to {@link #reindex()} will update the index to the current contents of * the {@link Collection}. * * @param the type of object in the index. * @param the type of object in the collection. * * @since 4.0 * @version $Id: IndexedCollection.java 1543740 2013-11-20 09:33:14Z ebourg $ */ public class IndexedCollection extends AbstractCollectionDecorator { /** Serialization version */ private static final long serialVersionUID = -5512610452568370038L; /** The {@link Transformer} for generating index keys. */ private final Transformer keyTransformer; /** The map of indexes to collected objects. */ private final MultiMap index; /** The uniqueness constraint for the index. */ private final boolean uniqueIndex; /** * Create an {@link IndexedCollection} for a unique index. *

* If an element is added, which maps to an existing key, an {@link IllegalArgumentException} * will be thrown. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection uniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), true); } /** * Create an {@link IndexedCollection} for a non-unique index. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection nonUniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), false); } /** * Create a {@link IndexedCollection}. * * @param coll decorated {@link Collection} * @param keyTransformer {@link Transformer} for generating index keys * @param map map to use as index * @param uniqueIndex if the index shall enforce uniqueness of index keys */ public IndexedCollection(final Collection coll, final Transformer keyTransformer, final MultiMap map, final boolean uniqueIndex) { super(coll); this.keyTransformer = keyTransformer; this.index = map; this.uniqueIndex = uniqueIndex; reindex(); } /** * {@inheritDoc} * * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ @Override public boolean add(final C object) { final boolean added = super.add(object); if (added) { addToIndex(object); } return added; } @Override public boolean addAll(final Collection coll) { boolean changed = false; for (final C c: coll) { changed |= add(c); } return changed; } @Override public void clear() { super.clear(); index.clear(); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @SuppressWarnings("unchecked") @Override public boolean contains(final Object object) { return index.containsKey(keyTransformer.transform((C) object)); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @Override public boolean containsAll(final Collection coll) { for (final Object o : coll) { if (!contains(o)) { return false; } } return true; } /** * Get the element associated with the given key. *

* In case of a non-unique index, this method will return the first * value associated with the given key. To retrieve all elements associated * with a key, use {@link #values(Object)}. * * @param key key to look up * @return element found * @see #values(Object) */ public C get(final K key) { @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection final Collection coll = (Collection) index.get(key); return coll == null ? null : coll.iterator().next(); } /** * Get all elements associated with the given key. * * @param key key to look up * @return a collection of elements found, or null if {@code contains(key) == false} */ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection public Collection values(final K key) { return (Collection) index.get(key); } /** * Clears the index and re-indexes the entire decorated {@link Collection}. */ public void reindex() { index.clear(); for (final C c : decorated()) { addToIndex(c); } } @SuppressWarnings("unchecked") @Override public boolean remove(final Object object) { final boolean removed = super.remove(object); if (removed) { removeFromIndex((C) object); } return removed; } @Override public boolean removeAll(final Collection coll) { boolean changed = false; for (final Object o : coll) { changed |= remove(o); } return changed; } @Override public boolean retainAll(final Collection coll) { final boolean changed = super.retainAll(coll); if (changed) { reindex(); } return changed; } //----------------------------------------------------------------------- /** * Provides checking for adding the index. * * @param object the object to index * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ private void addToIndex(final C object) { final K key = keyTransformer.transform(object); if (uniqueIndex && index.containsKey(key)) { throw new IllegalArgumentException("Duplicate key in uniquely indexed collection."); } index.put(key, object); } /** * Removes an object from the index. * * @param object the object to remove */ private void removeFromIndex(final C object) { index.remove(keyTransformer.transform(object)); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 7721; to = 7717.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.collection; import java.util.Collection; import java.util.HashMap; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.MultiValueMap; /** * An IndexedCollection is a Map-like view onto a Collection. It accepts a * keyTransformer to define how the keys are converted from the values. *

* Modifications made to this decorator modify the index as well as the * decorated {@link Collection}. However, modifications to the underlying * {@link Collection} will not update the index and it will get out of sync. *

* If modification of the decorated {@link Collection} is unavoidable, then a * call to {@link #reindex()} will update the index to the current contents of * the {@link Collection}. * * @param the type of object in the index. * @param the type of object in the collection. * * @since 4.0 * @version $Id: IndexedCollection.java 1543740 2013-11-20 09:33:14Z ebourg $ */ public class IndexedCollection extends AbstractCollectionDecorator { /** Serialization version */ private static final long serialVersionUID = -5512610452568370038L; /** The {@link Transformer} for generating index keys. */ private final Transformer keyTransformer; /** The map of indexes to collected objects. */ private final MultiMap index; /** The uniqueness constraint for the index. */ private final boolean uniqueIndex; /** * Create an {@link IndexedCollection} for a unique index. *

* If an element is added, which maps to an existing key, an {@link IllegalArgumentException} * will be thrown. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection uniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), true); } /** * Create an {@link IndexedCollection} for a non-unique index. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection nonUniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), false); } /** * Create a {@link IndexedCollection}. * * @param coll decorated {@link Collection} * @param keyTransformer {@link Transformer} for generating index keys * @param map map to use as index * @param uniqueIndex if the index shall enforce uniqueness of index keys */ public IndexedCollection(final Collection coll, final Transformer keyTransformer, final MultiMap map, final boolean uniqueIndex) { super(coll); this.keyTransformer = keyTransformer; this.index = map; this.uniqueIndex = uniqueIndex; reindex(); } /** * {@inheritDoc} * * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ @Override public boolean add(final C object) { final boolean added = super.add(object); if (added) { addToIndex(object); } return added; } @Override public boolean addAll(final Collection coll) { boolean changed = false; for (final C c: coll) { changed |= add(c); } return changed; } @Override public void clear() { super.clear(); index.clear(); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @SuppressWarnings("unchecked") @Override public boolean contains(final Object object) { return index.containsKey(keyTransformer.transform((C) object)); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @Override public boolean containsAll(final Collection coll) { for (final Object o : coll) { if (!contains(o)) { return false; } } return true; } /** * Get the element associated with the given key. *

* In case of a non-unique index, this method will return the first * value associated with the given key. To retrieve all elements associated * with a key, use {@link #values(Object)}. * * @param key key to look up * @return element found * @see #values(Object) */ public C get(final K key) { @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection final Collection coll = (Collection) index.get(key); return coll == null ? null : coll.iterator().next(); } /** * Get all elements associated with the given key. * * @param key key to look up * @return a collection of elements found, or null if {@code contains(key) == false} */ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection public Collection values(final K key) { return (Collection) index.get(key); } /** * Clears the index and re-indexes the entire decorated {@link Collection}. */ public void reindex() { index.clear(); for (final C c : decorated()) { addToIndex(c); } } @SuppressWarnings("unchecked") @Override public boolean remove(final Object object) { final boolean removed = super.remove(object); if (removed) { removeFromIndex((C) object); } return removed; } @Override public boolean removeAll(final Collection coll) { boolean changed = false; for (final Object o : coll) { changed |= remove(o); } return changed; } @Override public boolean retainAll(final Collection coll) { final boolean changed = super.retainAll(coll); if (changed) { reindex(); } return changed; } //----------------------------------------------------------------------- /** * Provides checking for adding the index. * * @param object the object to index * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ private void addToIndex(final C object) { final K key = keyTransformer.transform(object); if (uniqueIndex && index.containsKey(key)) { throw new IllegalArgumentException("Duplicate key in uniquely indexed collection."); } index.put(key, object); } /** * Removes an object from the index. * * @param object the object to remove */ private void removeFromIndex(final C object) { index.remove(keyTransformer.transform(object)); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 5098; to = 5094.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.collection; import java.util.Collection; import java.util.HashMap; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.MultiValueMap; /** * An IndexedCollection is a Map-like view onto a Collection. It accepts a * keyTransformer to define how the keys are converted from the values. *

* Modifications made to this decorator modify the index as well as the * decorated {@link Collection}. However, modifications to the underlying * {@link Collection} will not update the index and it will get out of sync. *

* If modification of the decorated {@link Collection} is unavoidable, then a * call to {@link #reindex()} will update the index to the current contents of * the {@link Collection}. * * @param the type of object in the index. * @param the type of object in the collection. * * @since 4.0 * @version $Id: IndexedCollection.java 1543740 2013-11-20 09:33:14Z ebourg $ */ public class IndexedCollection extends AbstractCollectionDecorator { /** Serialization version */ private static final long serialVersionUID = -5512610452568370038L; /** The {@link Transformer} for generating index keys. */ private final Transformer keyTransformer; /** The map of indexes to collected objects. */ private final MultiMap index; /** The uniqueness constraint for the index. */ private final boolean uniqueIndex; /** * Create an {@link IndexedCollection} for a unique index. *

* If an element is added, which maps to an existing key, an {@link IllegalArgumentException} * will be thrown. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection uniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), true); } /** * Create an {@link IndexedCollection} for a non-unique index. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection nonUniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), false); } /** * Create a {@link IndexedCollection}. * * @param coll decorated {@link Collection} * @param keyTransformer {@link Transformer} for generating index keys * @param map map to use as index * @param uniqueIndex if the index shall enforce uniqueness of index keys */ public IndexedCollection(final Collection coll, final Transformer keyTransformer, final MultiMap map, final boolean uniqueIndex) { super(coll); this.keyTransformer = keyTransformer; this.index = map; this.uniqueIndex = uniqueIndex; reindex(); } /** * {@inheritDoc} * * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ @Override public boolean add(final C object) { final boolean added = super.add(object); if (added) { addToIndex(object); } return added; } @Override public boolean addAll(final Collection coll) { boolean changed = false; for (final C c: coll) { changed |= add(c); } return changed; } @Override public void clear() { super.clear(); index.clear(); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @SuppressWarnings("unchecked") @Override public boolean contains(final Object object) { return index.containsKey(keyTransformer.transform((C) object)); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @Override public boolean containsAll(final Collection coll) { for (final Object o : coll) { if (!contains(o)) { return false; } } return true; } /** * Get the element associated with the given key. *

* In case of a non-unique index, this method will return the first * value associated with the given key. To retrieve all elements associated * with a key, use {@link #values(Object)}. * * @param key key to look up * @return element found * @see #values(Object) */ public C get(final K key) { @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection final Collection coll = (Collection) index.get(key); return coll == null ? null : coll.iterator().next(); } /** * Get all elements associated with the given key. * * @param key key to look up * @return a collection of elements found, or null if {@code contains(key) == false} */ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection public Collection values(final K key) { return (Collection) index.get(key); } /** * Clears the index and re-indexes the entire decorated {@link Collection}. */ public void reindex() { index.clear(); for (final C c : decorated()) { addToIndex(c); } } @SuppressWarnings("unchecked") @Override public boolean remove(final Object object) { final boolean removed = super.remove(object); if (removed) { removeFromIndex((C) object); } return removed; } @Override public boolean removeAll(final Collection coll) { boolean changed = false; for (final Object o : coll) { changed |= remove(o); } return changed; } @Override public boolean retainAll(final Collection coll) { final boolean changed = super.retainAll(coll); if (changed) { reindex(); } return changed; } //----------------------------------------------------------------------- /** * Provides checking for adding the index. * * @param object the object to index * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ private void addToIndex(final C object) { final K key = keyTransformer.transform(object); if (uniqueIndex && index.containsKey(key)) { throw new IllegalArgumentException("Duplicate key in uniquely indexed collection."); } index.put(key, object); } /** * Removes an object from the index. * * @param object the object to remove */ private void removeFromIndex(final C object) { index.remove(keyTransformer.transform(object)); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 5769; to = 5765.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.collection; import java.util.Collection; import java.util.HashMap; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.MultiValueMap; /** * An IndexedCollection is a Map-like view onto a Collection. It accepts a * keyTransformer to define how the keys are converted from the values. *

* Modifications made to this decorator modify the index as well as the * decorated {@link Collection}. However, modifications to the underlying * {@link Collection} will not update the index and it will get out of sync. *

* If modification of the decorated {@link Collection} is unavoidable, then a * call to {@link #reindex()} will update the index to the current contents of * the {@link Collection}. * * @param the type of object in the index. * @param the type of object in the collection. * * @since 4.0 * @version $Id: IndexedCollection.java 1543740 2013-11-20 09:33:14Z ebourg $ */ public class IndexedCollection extends AbstractCollectionDecorator { /** Serialization version */ private static final long serialVersionUID = -5512610452568370038L; /** The {@link Transformer} for generating index keys. */ private final Transformer keyTransformer; /** The map of indexes to collected objects. */ private final MultiMap index; /** The uniqueness constraint for the index. */ private final boolean uniqueIndex; /** * Create an {@link IndexedCollection} for a unique index. *

* If an element is added, which maps to an existing key, an {@link IllegalArgumentException} * will be thrown. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection uniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), true); } /** * Create an {@link IndexedCollection} for a non-unique index. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection nonUniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), false); } /** * Create a {@link IndexedCollection}. * * @param coll decorated {@link Collection} * @param keyTransformer {@link Transformer} for generating index keys * @param map map to use as index * @param uniqueIndex if the index shall enforce uniqueness of index keys */ public IndexedCollection(final Collection coll, final Transformer keyTransformer, final MultiMap map, final boolean uniqueIndex) { super(coll); this.keyTransformer = keyTransformer; this.index = map; this.uniqueIndex = uniqueIndex; reindex(); } /** * {@inheritDoc} * * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ @Override public boolean add(final C object) { final boolean added = super.add(object); if (added) { addToIndex(object); } return added; } @Override public boolean addAll(final Collection coll) { boolean changed = false; for (final C c: coll) { changed |= add(c); } return changed; } @Override public void clear() { super.clear(); index.clear(); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @SuppressWarnings("unchecked") @Override public boolean contains(final Object object) { return index.containsKey(keyTransformer.transform((C) object)); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @Override public boolean containsAll(final Collection coll) { for (final Object o : coll) { if (!contains(o)) { return false; } } return true; } /** * Get the element associated with the given key. *

* In case of a non-unique index, this method will return the first * value associated with the given key. To retrieve all elements associated * with a key, use {@link #values(Object)}. * * @param key key to look up * @return element found * @see #values(Object) */ public C get(final K key) { @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection final Collection coll = (Collection) index.get(key); return coll == null ? null : coll.iterator().next(); } /** * Get all elements associated with the given key. * * @param key key to look up * @return a collection of elements found, or null if {@code contains(key) == false} */ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection public Collection values(final K key) { return (Collection) index.get(key); } /** * Clears the index and re-indexes the entire decorated {@link Collection}. */ public void reindex() { index.clear(); for (final C c : decorated()) { addToIndex(c); } } @SuppressWarnings("unchecked") @Override public boolean remove(final Object object) { final boolean removed = super.remove(object); if (removed) { removeFromIndex((C) object); } return removed; } @Override public boolean removeAll(final Collection coll) { boolean changed = false; for (final Object o : coll) { changed |= remove(o); } return changed; } @Override public boolean retainAll(final Collection coll) { final boolean changed = super.retainAll(coll); if (changed) { reindex(); } return changed; } //----------------------------------------------------------------------- /** * Provides checking for adding the index. * * @param object the object to index * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ private void addToIndex(final C object) { final K key = keyTransformer.transform(object); if (uniqueIndex && index.containsKey(key)) { throw new IllegalArgumentException("Duplicate key in uniquely indexed collection."); } index.put(key, object); } /** * Removes an object from the index. * * @param object the object to remove */ private void removeFromIndex(final C object) { index.remove(keyTransformer.transform(object)); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 6911; to = 6826.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.collection; import java.util.Collection; import java.util.HashMap; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.MultiValueMap; /** * An IndexedCollection is a Map-like view onto a Collection. It accepts a * keyTransformer to define how the keys are converted from the values. *

* Modifications made to this decorator modify the index as well as the * decorated {@link Collection}. However, modifications to the underlying * {@link Collection} will not update the index and it will get out of sync. *

* If modification of the decorated {@link Collection} is unavoidable, then a * call to {@link #reindex()} will update the index to the current contents of * the {@link Collection}. * * @param the type of object in the index. * @param the type of object in the collection. * * @since 4.0 * @version $Id: IndexedCollection.java 1543740 2013-11-20 09:33:14Z ebourg $ */ public class IndexedCollection extends AbstractCollectionDecorator { /** Serialization version */ private static final long serialVersionUID = -5512610452568370038L; /** The {@link Transformer} for generating index keys. */ private final Transformer keyTransformer; /** The map of indexes to collected objects. */ private final MultiMap index; /** The uniqueness constraint for the index. */ private final boolean uniqueIndex; /** * Create an {@link IndexedCollection} for a unique index. *

* If an element is added, which maps to an existing key, an {@link IllegalArgumentException} * will be thrown. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection uniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), true); } /** * Create an {@link IndexedCollection} for a non-unique index. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection nonUniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), false); } /** * Create a {@link IndexedCollection}. * * @param coll decorated {@link Collection} * @param keyTransformer {@link Transformer} for generating index keys * @param map map to use as index * @param uniqueIndex if the index shall enforce uniqueness of index keys */ public IndexedCollection(final Collection coll, final Transformer keyTransformer, final MultiMap map, final boolean uniqueIndex) { super(coll); this.keyTransformer = keyTransformer; this.index = map; this.uniqueIndex = uniqueIndex; reindex(); } /** * {@inheritDoc} * * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ @Override public boolean add(final C object) { final boolean added = super.add(object); if (added) { addToIndex(object); } return added; } @Override public boolean addAll(final Collection coll) { boolean changed = false; for (final C c: coll) { changed |= add(c); } return changed; } @Override public void clear() { super.clear(); index.clear(); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @SuppressWarnings("unchecked") @Override public boolean contains(final Object object) { return index.containsKey(keyTransformer.transform((C) object)); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @Override public boolean containsAll(final Collection coll) { for (final Object o : coll) { if (!contains(o)) { return false; } } return true; } /** * Get the element associated with the given key. *

* In case of a non-unique index, this method will return the first * value associated with the given key. To retrieve all elements associated * with a key, use {@link #values(Object)}. * * @param key key to look up * @return element found * @see #values(Object) */ public C get(final K key) { @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection final Collection coll = (Collection) index.get(key); return coll == null ? null : coll.iterator().next(); } /** * Get all elements associated with the given key. * * @param key key to look up * @return a collection of elements found, or null if {@code contains(key) == false} */ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection public Collection values(final K key) { return (Collection) index.get(key); } /** * Clears the index and re-indexes the entire decorated {@link Collection}. */ public void reindex() { index.clear(); for (final C c : decorated()) { addToIndex(c); } } @SuppressWarnings("unchecked") @Override public boolean remove(final Object object) { final boolean removed = super.remove(object); if (removed) { removeFromIndex((C) object); } return removed; } @Override public boolean removeAll(final Collection coll) { boolean changed = false; for (final Object o : coll) { changed |= remove(o); } return changed; } @Override public boolean retainAll(final Collection coll) { final boolean changed = super.retainAll(coll); if (changed) { reindex(); } return changed; } //----------------------------------------------------------------------- /** * Provides checking for adding the index. * * @param object the object to index * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ private void addToIndex(final C object) { final K key = keyTransformer.transform(object); if (uniqueIndex && index.containsKey(key)) { throw new IllegalArgumentException("Duplicate key in uniquely indexed collection."); } index.put(key, object); } /** * Removes an object from the index. * * @param object the object to remove */ private void removeFromIndex(final C object) { index.remove(keyTransformer.transform(object)); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 7504; to = 7500.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.collection; import java.util.Collection; import java.util.HashMap; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.map.MultiValueMap; /** * An IndexedCollection is a Map-like view onto a Collection. It accepts a * keyTransformer to define how the keys are converted from the values. *

* Modifications made to this decorator modify the index as well as the * decorated {@link Collection}. However, modifications to the underlying * {@link Collection} will not update the index and it will get out of sync. *

* If modification of the decorated {@link Collection} is unavoidable, then a * call to {@link #reindex()} will update the index to the current contents of * the {@link Collection}. * * @param the type of object in the index. * @param the type of object in the collection. * * @since 4.0 * @version $Id: IndexedCollection.java 1543740 2013-11-20 09:33:14Z ebourg $ */ public class IndexedCollection extends AbstractCollectionDecorator { /** Serialization version */ private static final long serialVersionUID = -5512610452568370038L; /** The {@link Transformer} for generating index keys. */ private final Transformer keyTransformer; /** The map of indexes to collected objects. */ private final MultiMap index; /** The uniqueness constraint for the index. */ private final boolean uniqueIndex; /** * Create an {@link IndexedCollection} for a unique index. *

* If an element is added, which maps to an existing key, an {@link IllegalArgumentException} * will be thrown. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection uniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), true); } /** * Create an {@link IndexedCollection} for a non-unique index. * * @param the index object type. * @param the collection type. * @param coll the decorated {@link Collection}. * @param keyTransformer the {@link Transformer} for generating index keys. * @return the created {@link IndexedCollection}. */ public static IndexedCollection nonUniqueIndexedCollection(final Collection coll, final Transformer keyTransformer) { return new IndexedCollection(coll, keyTransformer, MultiValueMap.multiValueMap(new HashMap>()), false); } /** * Create a {@link IndexedCollection}. * * @param coll decorated {@link Collection} * @param keyTransformer {@link Transformer} for generating index keys * @param map map to use as index * @param uniqueIndex if the index shall enforce uniqueness of index keys */ public IndexedCollection(final Collection coll, final Transformer keyTransformer, final MultiMap map, final boolean uniqueIndex) { super(coll); this.keyTransformer = keyTransformer; this.index = map; this.uniqueIndex = uniqueIndex; reindex(); } /** * {@inheritDoc} * * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ @Override public boolean add(final C object) { final boolean added = super.add(object); if (added) { addToIndex(object); } return added; } @Override public boolean addAll(final Collection coll) { boolean changed = false; for (final C c: coll) { changed |= add(c); } return changed; } @Override public void clear() { super.clear(); index.clear(); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @SuppressWarnings("unchecked") @Override public boolean contains(final Object object) { return index.containsKey(keyTransformer.transform((C) object)); } /** * {@inheritDoc} *

* Note: uses the index for fast lookup */ @Override public boolean containsAll(final Collection coll) { for (final Object o : coll) { if (!contains(o)) { return false; } } return true; } /** * Get the element associated with the given key. *

* In case of a non-unique index, this method will return the first * value associated with the given key. To retrieve all elements associated * with a key, use {@link #values(Object)}. * * @param key key to look up * @return element found * @see #values(Object) */ public C get(final K key) { @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection final Collection coll = (Collection) index.get(key); return coll == null ? null : coll.iterator().next(); } /** * Get all elements associated with the given key. * * @param key key to look up * @return a collection of elements found, or null if {@code contains(key) == false} */ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection public Collection values(final K key) { return (Collection) index.get(key); } /** * Clears the index and re-indexes the entire decorated {@link Collection}. */ public void reindex() { index.clear(); for (final C c : decorated()) { addToIndex(c); } } @SuppressWarnings("unchecked") @Override public boolean remove(final Object object) { final boolean removed = super.remove(object); if (removed) { removeFromIndex((C) object); } return removed; } @Override public boolean removeAll(final Collection coll) { boolean changed = false; for (final Object o : coll) { changed |= remove(o); } return changed; } @Override public boolean retainAll(final Collection coll) { final boolean changed = super.retainAll(coll); if (changed) { reindex(); } return changed; } //----------------------------------------------------------------------- /** * Provides checking for adding the index. * * @param object the object to index * @throws IllegalArgumentException if the object maps to an existing key and the index * enforces a uniqueness constraint */ private void addToIndex(final C object) { final K key = keyTransformer.transform(object); if (uniqueIndex && index.containsKey(key)) { throw new IllegalArgumentException("Duplicate key in uniquely indexed collection."); } index.put(key, object); } /** * Removes an object from the index. * * @param object the object to remove */ private void removeFromIndex(final C object) { index.remove(keyTransformer.transform(object)); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 7721; to = 7717.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.bag.HashBag; import org.apache.commons.collections4.collection.PredicatedCollection; import org.apache.commons.collections4.collection.SynchronizedCollection; import org.apache.commons.collections4.collection.TransformedCollection; import org.apache.commons.collections4.collection.UnmodifiableBoundedCollection; import org.apache.commons.collections4.collection.UnmodifiableCollection; import org.apache.commons.collections4.functors.TruePredicate; import org.apache.commons.collections4.iterators.CollatingIterator; import org.apache.commons.collections4.iterators.PermutationIterator; /** * Provides utility methods and decorators for {@link Collection} instances. *

* NOTE: From 4.0, method parameters will take {@link Iterable} objects when possible. * * @since 1.0 * @version $Id: CollectionUtils.java 1540639 2013-11-11 08:54:12Z tn $ */ public class CollectionUtils { /** * Helper class to easily access cardinality properties of two collections. * @param the element type */ private static class CardinalityHelper { /** Contains the cardinality for each object in collection A. */ final Map cardinalityA; /** Contains the cardinality for each object in collection B. */ final Map cardinalityB; /** * Create a new CardinalityHelper for two collections. * @param a the first collection * @param b the second collection */ public CardinalityHelper(final Iterable a, final Iterable b) { cardinalityA = CollectionUtils.getCardinalityMap(a); cardinalityB = CollectionUtils.getCardinalityMap(b); } /** * Returns the maximum frequency of an object. * @param obj the object * @return the maximum frequency of the object */ public final int max(final Object obj) { return Math.max(freqA(obj), freqB(obj)); } /** * Returns the minimum frequency of an object. * @param obj the object * @return the minimum frequency of the object */ public final int min(final Object obj) { return Math.min(freqA(obj), freqB(obj)); } /** * Returns the frequency of this object in collection A. * @param obj the object * @return the frequency of the object in collection A */ public int freqA(final Object obj) { return getFreq(obj, cardinalityA); } /** * Returns the frequency of this object in collection B. * @param obj the object * @return the frequency of the object in collection B */ public int freqB(final Object obj) { return getFreq(obj, cardinalityB); } private final int getFreq(final Object obj, final Map freqMap) { final Integer count = freqMap.get(obj); if (count != null) { return count.intValue(); } return 0; } } /** * Helper class for set-related operations, e.g. union, subtract, intersection. * @param the element type */ private static class SetOperationCardinalityHelper extends CardinalityHelper implements Iterable { /** Contains the unique elements of the two collections. */ private final Set elements; /** Output collection. */ private final List newList; /** * Create a new set operation helper from the two collections. * @param a the first collection * @param b the second collection */ public SetOperationCardinalityHelper(final Iterable a, final Iterable b) { super(a, b); elements = new HashSet(); addAll(elements, a); addAll(elements, b); // the resulting list must contain at least each unique element, but may grow newList = new ArrayList(elements.size()); } public Iterator iterator() { return elements.iterator(); } /** * Add the object {@code count} times to the result collection. * @param obj the object to add * @param count the count */ public void setCardinality(final O obj, final int count) { for (int i = 0; i < count; i++) { newList.add(obj); } } /** * Returns the resulting collection. * @return the result */ public Collection list() { return newList; } } /** * An empty unmodifiable collection. * The JDK provides empty Set and List implementations which could be used for * this purpose. However they could be cast to Set or List which might be * undesirable. This implementation only implements Collection. */ @SuppressWarnings("rawtypes") // we deliberately use the raw type here public static final Collection EMPTY_COLLECTION = UnmodifiableCollection.unmodifiableCollection(new ArrayList()); /** * CollectionUtils should not normally be instantiated. */ private CollectionUtils() {} /** * Returns the immutable EMPTY_COLLECTION with generic type safety. * * @see #EMPTY_COLLECTION * @since 4.0 * @param the element type * @return immutable empty collection */ @SuppressWarnings("unchecked") // OK, empty collection is compatible with any type public static Collection emptyCollection() { return EMPTY_COLLECTION; } /** * Returns an immutable empty collection if the argument is null, * or the argument itself otherwise. * * @param the element type * @param collection the collection, possibly null * @return an empty collection if the argument is null */ @SuppressWarnings("unchecked") // OK, empty collection is compatible with any type public static Collection emptyIfNull(final Collection collection) { return collection == null ? EMPTY_COLLECTION : collection; } /** * Returns a {@link Collection} containing the union of the given * {@link Iterable}s. *

* The cardinality of each element in the returned {@link Collection} will * be equal to the maximum of the cardinality of that element in the two * given {@link Iterable}s. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the union of the two collections * @see Collection#addAll */ public static Collection union(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.max(obj)); } return helper.list(); } /** * Returns a {@link Collection} containing the intersection of the given * {@link Iterable}s. *

* The cardinality of each element in the returned {@link Collection} will * be equal to the minimum of the cardinality of that element in the two * given {@link Iterable}s. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the intersection of the two collections * @see Collection#retainAll * @see #containsAny */ public static Collection intersection(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.min(obj)); } return helper.list(); } /** * Returns a {@link Collection} containing the exclusive disjunction * (symmetric difference) of the given {@link Iterable}s. *

* The cardinality of each element e in the returned * {@link Collection} will be equal to * max(cardinality(e,a),cardinality(e,b)) - min(cardinality(e,a), * cardinality(e,b)). *

* This is equivalent to * {@link #subtract subtract}({@link #union union(a,b)},{@link #intersection intersection(a,b)}) * or * {@link #union union}({@link #subtract subtract(a,b)},{@link #subtract subtract(b,a)}). * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the symmetric difference of the two collections */ public static Collection disjunction(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.max(obj) - helper.min(obj)); } return helper.list(); } /** * Returns a new {@link Collection} containing a - b. * The cardinality of each element e in the returned {@link Collection} * will be the cardinality of e in a minus the cardinality * of e in b, or zero, whichever is greater. * * @param a the collection to subtract from, must not be null * @param b the collection to subtract, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return a new collection with the results * @see Collection#removeAll */ public static Collection subtract(final Iterable a, final Iterable b) { final Predicate p = TruePredicate.truePredicate(); return subtract(a, b, p); } /** * Returns a new {@link Collection} containing a minus a subset of * b. Only the elements of b that satisfy the predicate * condition, p are subtracted from a. * *

The cardinality of each element e in the returned {@link Collection} * that satisfies the predicate condition will be the cardinality of e in a * minus the cardinality of e in b, or zero, whichever is greater.

*

The cardinality of each element e in the returned {@link Collection} that does not * satisfy the predicate condition will be equal to the cardinality of e in a.

* * @param a the collection to subtract from, must not be null * @param b the collection to subtract, must not be null * @param p the condition used to determine which elements of b are * subtracted. * @param the generic type that is able to represent the types contained * in both input collections. * @return a new collection with the results * @since 4.0 * @see Collection#removeAll */ public static Collection subtract(final Iterable a, final Iterable b, final Predicate p) { final ArrayList list = new ArrayList(); final HashBag bag = new HashBag(); for (final O element : b) { if (p.evaluate(element)) { bag.add(element); } } for (final O element : a) { if (!bag.remove(element, 1)) { list.add(element); } } return list; } /** * Returns true iff all elements of {@code coll2} are also contained * in {@code coll1}. The cardinality of values in {@code coll2} is not taken into account, * which is the same behavior as {@link Collection#containsAll(Collection)}. *

* In other words, this method returns true iff the * {@link #intersection} of coll1 and coll2 has the same cardinality as * the set of unique values from {@code coll2}. In case {@code coll2} is empty, {@code true} * will be returned. *

* This method is intended as a replacement for {@link Collection#containsAll(Collection)} * with a guaranteed runtime complexity of {@code O(n + m)}. Depending on the type of * {@link Collection} provided, this method will be much faster than calling * {@link Collection#containsAll(Collection)} instead, though this will come at the * cost of an additional space complexity O(n). * * @param coll1 the first collection, must not be null * @param coll2 the second collection, must not be null * @return true iff the intersection of the collections has the same cardinality * as the set of unique elements from the second collection * @since 4.0 */ public static boolean containsAll(final Collection coll1, final Collection coll2) { if (coll2.isEmpty()) { return true; } else { final Iterator it = coll1.iterator(); final Set elementsAlreadySeen = new HashSet(); for (final Object nextElement : coll2) { if (elementsAlreadySeen.contains(nextElement)) { continue; } boolean foundCurrentElement = false; while (it.hasNext()) { final Object p = it.next(); elementsAlreadySeen.add(p); if (nextElement == null ? p == null : nextElement.equals(p)) { foundCurrentElement = true; break; } } if (foundCurrentElement) { continue; } else { return false; } } return true; } } /** * Returns true iff at least one element is in both collections. *

* In other words, this method returns true iff the * {@link #intersection} of coll1 and coll2 is not empty. * * @param coll1 the first collection, must not be null * @param coll2 the second collection, must not be null * @return true iff the intersection of the collections is non-empty * @since 2.1 * @see #intersection */ public static boolean containsAny(final Collection coll1, final Collection coll2) { if (coll1.size() < coll2.size()) { for (final Object aColl1 : coll1) { if (coll2.contains(aColl1)) { return true; } } } else { for (final Object aColl2 : coll2) { if (coll1.contains(aColl2)) { return true; } } } return false; } /** * Returns a {@link Map} mapping each unique element in the given * {@link Collection} to an {@link Integer} representing the number * of occurrences of that element in the {@link Collection}. *

* Only those elements present in the collection will appear as * keys in the map. * * @param the type of object in the returned {@link Map}. This is a super type of . * @param coll the collection to get the cardinality map for, must not be null * @return the populated cardinality map */ public static Map getCardinalityMap(final Iterable coll) { final Map count = new HashMap(); for (final O obj : coll) { final Integer c = count.get(obj); if (c == null) { count.put(obj, Integer.valueOf(1)); } else { count.put(obj, Integer.valueOf(c.intValue() + 1)); } } return count; } /** * Returns true iff a is a sub-collection of b, * that is, iff the cardinality of e in a is less than or * equal to the cardinality of e in b, for each element e * in a. * * @param a the first (sub?) collection, must not be null * @param b the second (super?) collection, must not be null * @return true iff a is a sub-collection of b * @see #isProperSubCollection * @see Collection#containsAll */ public static boolean isSubCollection(final Collection a, final Collection b) { final CardinalityHelper helper = new CardinalityHelper(a, b); for (final Object obj : a) { if (helper.freqA(obj) > helper.freqB(obj)) { return false; } } return true; } /** * Returns true iff a is a proper sub-collection of b, * that is, iff the cardinality of e in a is less * than or equal to the cardinality of e in b, * for each element e in a, and there is at least one * element f such that the cardinality of f in b * is strictly greater than the cardinality of f in a. *

* The implementation assumes *

    *
  • a.size() and b.size() represent the * total cardinality of a and b, resp.
  • *
  • a.size() < Integer.MAXVALUE
  • *
* * @param a the first (sub?) collection, must not be null * @param b the second (super?) collection, must not be null * @return true iff a is a proper sub-collection of b * @see #isSubCollection * @see Collection#containsAll */ public static boolean isProperSubCollection(final Collection a, final Collection b) { return a.size() < b.size() && CollectionUtils.isSubCollection(a, b); } /** * Returns true iff the given {@link Collection}s contain * exactly the same elements with exactly the same cardinalities. *

* That is, iff the cardinality of e in a is * equal to the cardinality of e in b, * for each element e in a or b. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @return true iff the collections contain the same elements with the same cardinalities. */ public static boolean isEqualCollection(final Collection a, final Collection b) { if(a.size() != b.size()) { return false; } final CardinalityHelper helper = new CardinalityHelper(a, b); if(helper.cardinalityA.size() != helper.cardinalityB.size()) { return false; } for( final Object obj : helper.cardinalityA.keySet()) { if(helper.freqA(obj) != helper.freqB(obj)) { return false; } } return true; } /** * Returns true iff the given {@link Collection}s contain * exactly the same elements with exactly the same cardinalities. *

* That is, iff the cardinality of e in a is * equal to the cardinality of e in b, * for each element e in a or b. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param equator the Equator used for testing equality * @return true iff the collections contain the same elements with the same cardinalities. * @throws IllegalArgumentException if the equator is null * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) // we don't know the types due to wildcards in the signature public static boolean isEqualCollection(final Collection a, final Collection b, final Equator equator) { if (equator == null) { throw new IllegalArgumentException("equator may not be null"); } if(a.size() != b.size()) { return false; } final Transformer transformer = new Transformer() { public EquatorWrapper transform(final Object input) { return new EquatorWrapper(equator, input); } }; return isEqualCollection(collect(a, transformer), collect(b, transformer)); } /** * Wraps another object and uses the provided Equator to implement * {@link #equals(Object)} and {@link #hashCode()}. *

* This class can be used to store objects into a Map. * * @param the element type * @since 4.0 */ private static class EquatorWrapper { private final Equator equator; private final O object; public EquatorWrapper(final Equator equator, final O object) { this.equator = equator; this.object = object; } public O getObject() { return object; } @Override public boolean equals(final Object obj) { if (!(obj instanceof EquatorWrapper)) { return false; } @SuppressWarnings("unchecked") final EquatorWrapper otherObj = (EquatorWrapper) obj; return equator.equate(object, otherObj.getObject()); } @Override public int hashCode() { return equator.hash(object); } } /** * Returns the number of occurrences of obj in coll. * * @param obj the object to find the cardinality of * @param coll the {@link Iterable} to search * @param the type of object that the {@link Iterable} may contain. * @return the the number of occurrences of obj in coll */ public static int cardinality(final O obj, final Iterable coll) { if (coll instanceof Set) { return ((Set) coll).contains(obj) ? 1 : 0; } if (coll instanceof Bag) { return ((Bag) coll).getCount(obj); } int count = 0; if (obj == null) { for (final Object element : coll) { if (element == null) { count++; } } } else { for (final Object element : coll) { if (obj.equals(element)) { count++; } } } return count; } /** * Finds the first element in the given collection which matches the given predicate. *

* If the input collection or predicate is null, or no element of the collection * matches the predicate, null is returned. * * @param the type of object the {@link Iterable} contains * @param collection the collection to search, may be null * @param predicate the predicate to use, may be null * @return the first element of the collection which matches the predicate or null if none could be found */ public static T find(final Iterable collection, final Predicate predicate) { if (collection != null && predicate != null) { for (final T item : collection) { if (predicate.evaluate(item)) { return item; } } } return null; } /** * Executes the given closure on each element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param the closure type * @param collection the collection to get the input from, may be null * @param closure the closure to perform, may be null * @return closure */ public static > C forAllDo(final Iterable collection, final C closure) { if (collection != null && closure != null) { for (final T element : collection) { closure.execute(element); } } return closure; } /** * Executes the given closure on each element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterator} contains * @param the closure type * @param iterator the iterator to get the input from, may be null * @param closure the closure to perform, may be null * @return closure * @since 4.0 */ public static > C forAllDo(final Iterator iterator, final C closure) { if (iterator != null && closure != null) { while (iterator.hasNext()) { closure.execute(iterator.next()); } } return closure; } /** * Executes the given closure on each but the last element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param the closure type * @param collection the collection to get the input from, may be null * @param closure the closure to perform, may be null * @return the last element in the collection, or null if either collection or closure is null * @since 4.0 */ public static > T forAllButLastDo(final Iterable collection, final C closure) { return collection != null && closure != null ? forAllButLastDo(collection.iterator(), closure) : null; } /** * Executes the given closure on each but the last element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Collection} contains * @param the closure type * @param iterator the iterator to get the input from, may be null * @param closure the closure to perform, may be null * @return the last element in the collection, or null if either iterator or closure is null * @since 4.0 */ public static > T forAllButLastDo(final Iterator iterator, final C closure) { if (iterator != null && closure != null) { while (iterator.hasNext()) { final T element = iterator.next(); if (iterator.hasNext()) { closure.execute(element); } else { return element; } } } return null; } /** * Filter the collection by applying a Predicate to each element. If the * predicate returns false, remove the element. *

* If the input collection or predicate is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param collection the collection to get the input from, may be null * @param predicate the predicate to use as a filter, may be null * @return true if the collection is modified by this call, false otherwise. */ public static boolean filter(final Iterable collection, final Predicate predicate) { boolean result = false; if (collection != null && predicate != null) { for (final Iterator it = collection.iterator(); it.hasNext();) { if (!predicate.evaluate(it.next())) { it.remove(); result = true; } } } return result; } /** * Filter the collection by applying a Predicate to each element. If the * predicate returns true, remove the element. *

* This is equivalent to

filter(collection, PredicateUtils.notPredicate(predicate))
* if predicate is != null. *

* If the input collection or predicate is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param collection the collection to get the input from, may be null * @param predicate the predicate to use as a filter, may be null * @return true if the collection is modified by this call, false otherwise. */ public static boolean filterInverse(final Iterable collection, final Predicate predicate) { return filter(collection, predicate == null ? null : PredicateUtils.notPredicate(predicate)); } /** * Transform the collection by applying a Transformer to each element. *

* If the input collection or transformer is null, there is no change made. *

* This routine is best for Lists, for which set() is used to do the * transformations "in place." For other Collections, clear() and addAll() * are used to replace elements. *

* If the input collection controls its input, such as a Set, and the * Transformer creates duplicates (or are otherwise invalid), the collection * may reduce in size due to calling this method. * * @param the type of object the {@link Collection} contains * @param collection the {@link Collection} to get the input from, may be null * @param transformer the transformer to perform, may be null */ public static void transform(final Collection collection, final Transformer transformer) { if (collection != null && transformer != null) { if (collection instanceof List) { final List list = (List) collection; for (final ListIterator it = list.listIterator(); it.hasNext();) { it.set(transformer.transform(it.next())); } } else { final Collection resultCollection = collect(collection, transformer); collection.clear(); collection.addAll(resultCollection); } } } /** * Counts the number of elements in the input collection that match the * predicate. *

* A null collection or predicate matches no elements. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return the number of matches for the predicate in the collection */ public static int countMatches(final Iterable input, final Predicate predicate) { int count = 0; if (input != null && predicate != null) { for (final C o : input) { if (predicate.evaluate(o)) { count++; } } } return count; } /** * Answers true if a predicate is true for at least one element of a * collection. *

* A null collection or predicate returns false. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return true if at least one element of the collection matches the predicate */ public static boolean exists(final Iterable input, final Predicate predicate) { if (input != null && predicate != null) { for (final C o : input) { if (predicate.evaluate(o)) { return true; } } } return false; } /** * Answers true if a predicate is true for every element of a * collection. *

* A null predicate returns false.
* A null or empty collection returns true. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return true if every element of the collection matches the predicate or if the * collection is empty, false otherwise * @since 4.0 */ public static boolean matchesAll(final Iterable input, final Predicate predicate) { if (predicate == null) { return false; } if (input != null) { for (final C o : input) { if (!predicate.evaluate(o)) { return false; } } } return true; } /** * Selects all elements from input collection which match the given * predicate into an output collection. *

* A null predicate matches no elements. * * @param the type of object the {@link Iterable} contains * @param inputCollection the collection to get the input from, may not be null * @param predicate the predicate to use, may be null * @return the elements matching the predicate (new list) * @throws NullPointerException if the input collection is null */ public static Collection select(final Iterable inputCollection, final Predicate predicate) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return select(inputCollection, predicate, answer); } /** * Selects all elements from input collection which match the given * predicate and adds them to outputCollection. *

* If the input collection or predicate is null, there is no change to the * output collection. * * @param the type of object the {@link Iterable} contains * @param the type of the output {@link Collection} * @param inputCollection the collection to get the input from, may be null * @param predicate the predicate to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and predicate or not null * @return the outputCollection */ public static > R select(final Iterable inputCollection, final Predicate predicate, final R outputCollection) { if (inputCollection != null && predicate != null) { for (final O item : inputCollection) { if (predicate.evaluate(item)) { outputCollection.add(item); } } } return outputCollection; } /** * Selects all elements from inputCollection which don't match the given * predicate into an output collection. *

* If the input predicate is null, the result is an empty * list. * * @param the type of object the {@link Iterable} contains * @param inputCollection the collection to get the input from, may not be null * @param predicate the predicate to use, may be null * @return the elements not matching the predicate (new list) * @throws NullPointerException if the input collection is null */ public static Collection selectRejected(final Iterable inputCollection, final Predicate predicate) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return selectRejected(inputCollection, predicate, answer); } /** * Selects all elements from inputCollection which don't match the given * predicate and adds them to outputCollection. *

* If the input predicate is null, no elements are added to * outputCollection. * * @param the type of object the {@link Iterable} contains * @param the type of the output {@link Collection} * @param inputCollection the collection to get the input from, may be null * @param predicate the predicate to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and predicate or not null * @return outputCollection */ public static > R selectRejected(final Iterable inputCollection, final Predicate predicate, final R outputCollection) { if (inputCollection != null && predicate != null) { for (final O item : inputCollection) { if (!predicate.evaluate(item)) { outputCollection.add(item); } } } return outputCollection; } /** * Returns a new Collection consisting of the elements of inputCollection * transformed by the given transformer. *

* If the input transformer is null, the result is an empty list. * * @param the type of object in the input collection * @param the type of object in the output collection * @param inputCollection the collection to get the input from, may not be null * @param transformer the transformer to use, may be null * @return the transformed result (new list) * @throws NullPointerException if the input collection is null */ public static Collection collect(final Iterable inputCollection, final Transformer transformer) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return collect(inputCollection, transformer, answer); } /** * Transforms all elements from the inputIterator with the given transformer * and adds them to the outputCollection. *

* If the input iterator or transformer is null, the result is an empty * list. * * @param inputIterator the iterator to get the input from, may be null * @param transformer the transformer to use, may be null * @param the type of object in the input collection * @param the type of object in the output collection * @return the transformed result (new list) */ public static Collection collect(final Iterator inputIterator, final Transformer transformer) { return collect(inputIterator, transformer, new ArrayList()); } /** * Transforms all elements from inputCollection with the given transformer * and adds them to the outputCollection. *

* If the input collection or transformer is null, there is no change to the * output collection. * * @param the type of object in the input collection * @param the type of object in the output collection * @param the output type of the transformer - this extends O. * @param inputCollection the collection to get the input from, may be null * @param transformer the transformer to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and transformer are not null * @return the outputCollection with the transformed input added * @throws NullPointerException if the output collection is null and both, inputCollection and * transformer are not null */ public static > R collect(final Iterable inputCollection, final Transformer transformer, final R outputCollection) { if (inputCollection != null) { return collect(inputCollection.iterator(), transformer, outputCollection); } return outputCollection; } /** * Transforms all elements from the inputIterator with the given transformer * and adds them to the outputCollection. *

* If the input iterator or transformer is null, there is no change to the * output collection. * * @param inputIterator the iterator to get the input from, may be null * @param transformer the transformer to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and transformer are not null * @param the type of object in the input collection * @param the type of object in the output collection * @param the output type of the transformer - this extends O. * @return the outputCollection with the transformed input added * @throws NullPointerException if the output collection is null and both, inputCollection and * transformer are not null */ public static > R collect(final Iterator inputIterator, final Transformer transformer, final R outputCollection) { if (inputIterator != null && transformer != null) { while (inputIterator.hasNext()) { final I item = inputIterator.next(); final O value = transformer.transform(item); outputCollection.add(value); } } return outputCollection; } //----------------------------------------------------------------------- /** * Adds an element to the collection unless the element is null. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param object the object to add, if null it will not be added * @return true if the collection changed * @throws NullPointerException if the collection is null * @since 3.2 */ public static boolean addIgnoreNull(final Collection collection, final T object) { if (collection == null) { throw new NullPointerException("The collection must not be null"); } return object != null && collection.add(object); } /** * Adds all elements in the {@link Iterable} to the given collection. If the * {@link Iterable} is a {@link Collection} then it is cast and will be * added using {@link Collection#addAll(Collection)} instead of iterating. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param iterable the iterable of elements to add, must not be null * @return a boolean indicating whether the collection has changed or not. * @throws NullPointerException if the collection or iterator is null */ public static boolean addAll(final Collection collection, final Iterable iterable) { if (iterable instanceof Collection) { return collection.addAll((Collection) iterable); } return addAll(collection, iterable.iterator()); } /** * Adds all elements in the iteration to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param iterator the iterator of elements to add, must not be null * @return a boolean indicating whether the collection has changed or not. * @throws NullPointerException if the collection or iterator is null */ public static boolean addAll(final Collection collection, final Iterator iterator) { boolean changed = false; while (iterator.hasNext()) { changed |= collection.add(iterator.next()); } return changed; } /** * Adds all elements in the enumeration to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param enumeration the enumeration of elements to add, must not be null * @return {@code true} if the collections was changed, {@code false} otherwise * @throws NullPointerException if the collection or enumeration is null */ public static boolean addAll(final Collection collection, final Enumeration enumeration) { boolean changed = false; while (enumeration.hasMoreElements()) { changed |= collection.add(enumeration.nextElement()); } return changed; } /** * Adds all elements in the array to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param elements the array of elements to add, must not be null * @return {@code true} if the collection was changed, {@code false} otherwise * @throws NullPointerException if the collection or array is null */ public static boolean addAll(final Collection collection, final C[] elements) { boolean changed = false; for (final C element : elements) { changed |= collection.add(element); } return changed; } /** * Returns the index-th value in {@link Iterator}, throwing * IndexOutOfBoundsException if there is no such element. *

* The Iterator is advanced to index (or to the end, if * index exceeds the number of entries) as a side effect of this method. * * @param iterator the iterator to get a value from * @param index the index to get * @param the type of object in the {@link Iterator} * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid * @throws IllegalArgumentException if the object type is invalid */ public static T get(final Iterator iterator, final int index) { int i = index; checkIndexBounds(i); while (iterator.hasNext()) { i--; if (i == -1) { return iterator.next(); } iterator.next(); } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } /** * Ensures an index is not negative. * @param index the index to check. * @throws IndexOutOfBoundsException if the index is negative. */ private static void checkIndexBounds(final int index) { if (index < 0) { throw new IndexOutOfBoundsException("Index cannot be negative: " + index); } } /** * Returns the index-th value in the iterable's {@link Iterator}, throwing * IndexOutOfBoundsException if there is no such element. *

* If the {@link Iterable} is a {@link List}, then it will use {@link List#get(int)}. * * @param iterable the {@link Iterable} to get a value from * @param index the index to get * @param the type of object in the {@link Iterable}. * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid */ public static T get(final Iterable iterable, final int index) { checkIndexBounds(index); if (iterable instanceof List) { return ((List) iterable).get(index); } return get(iterable.iterator(), index); } /** * Returns the index-th value in object, throwing * IndexOutOfBoundsException if there is no such element or * IllegalArgumentException if object is not an * instance of one of the supported types. *

* The supported types, and associated semantics are: *

    *
  • Map -- the value returned is the Map.Entry in position * index in the map's entrySet iterator, * if there is such an entry.
  • *
  • List -- this method is equivalent to the list's get method.
  • *
  • Array -- the index-th array entry is returned, * if there is such an entry; otherwise an IndexOutOfBoundsException * is thrown.
  • *
  • Collection -- the value returned is the index-th object * returned by the collection's default iterator, if there is such an element.
  • *
  • Iterator or Enumeration -- the value returned is the * index-th object in the Iterator/Enumeration, if there * is such an element. The Iterator/Enumeration is advanced to * index (or to the end, if index exceeds the * number of entries) as a side effect of this method.
  • *
* * @param object the object to get a value from * @param index the index to get * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid * @throws IllegalArgumentException if the object type is invalid */ public static Object get(final Object object, final int index) { int i = index; if (i < 0) { throw new IndexOutOfBoundsException("Index cannot be negative: " + i); } if (object instanceof Map) { final Map map = (Map) object; final Iterator iterator = map.entrySet().iterator(); return get(iterator, i); } else if (object instanceof Object[]) { return ((Object[]) object)[i]; } else if (object instanceof Iterator) { final Iterator it = (Iterator) object; while (it.hasNext()) { i--; if (i == -1) { return it.next(); } it.next(); } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } else if (object instanceof Collection) { final Iterator iterator = ((Collection) object).iterator(); return get(iterator, i); } else if (object instanceof Enumeration) { final Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { i--; if (i == -1) { return it.nextElement(); } else { it.nextElement(); } } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } else if (object == null) { throw new IllegalArgumentException("Unsupported object type: null"); } else { try { return Array.get(object, i); } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } /** * Returns the index-th Map.Entry in the map's entrySet, * throwing IndexOutOfBoundsException if there is no such element. * * @param the key type in the {@link Map} * @param the key type in the {@link Map} * @param map the object to get a value from * @param index the index to get * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid */ public static Map.Entry get(final Map map, final int index) { checkIndexBounds(index); return get(map.entrySet(), index); } /** * Gets the size of the collection/iterator specified. *

* This method can handles objects as follows *

    *
  • Collection - the collection size *
  • Map - the map size *
  • Array - the array size *
  • Iterator - the number of elements remaining in the iterator *
  • Enumeration - the number of elements remaining in the enumeration *
* * @param object the object to get the size of, may be null * @return the size of the specified collection or 0 if the object was null * @throws IllegalArgumentException thrown if object is not recognised * @since 3.1 */ public static int size(final Object object) { if (object == null) { return 0; } int total = 0; if (object instanceof Map) { total = ((Map) object).size(); } else if (object instanceof Collection) { total = ((Collection) object).size(); } else if (object instanceof Object[]) { total = ((Object[]) object).length; } else if (object instanceof Iterator) { final Iterator it = (Iterator) object; while (it.hasNext()) { total++; it.next(); } } else if (object instanceof Enumeration) { final Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { total++; it.nextElement(); } } else { try { total = Array.getLength(object); } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } return total; } /** * Checks if the specified collection/array/iterator is empty. *

* This method can handles objects as follows *

    *
  • Collection - via collection isEmpty *
  • Map - via map isEmpty *
  • Array - using array size *
  • Iterator - via hasNext *
  • Enumeration - via hasMoreElements *
*

* Note: This method is named to avoid clashing with * {@link #isEmpty(Collection)}. * * @param object the object to get the size of, may be null * @return true if empty or null * @throws IllegalArgumentException thrown if object is not recognised * @since 3.2 */ public static boolean sizeIsEmpty(final Object object) { if (object == null) { return true; } else if (object instanceof Collection) { return ((Collection) object).isEmpty(); } else if (object instanceof Map) { return ((Map) object).isEmpty(); } else if (object instanceof Object[]) { return ((Object[]) object).length == 0; } else if (object instanceof Iterator) { return ((Iterator) object).hasNext() == false; } else if (object instanceof Enumeration) { return ((Enumeration) object).hasMoreElements() == false; } else { try { return Array.getLength(object) == 0; } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } //----------------------------------------------------------------------- /** * Null-safe check if the specified collection is empty. *

* Null returns true. * * @param coll the collection to check, may be null * @return true if empty or null * @since 3.2 */ public static boolean isEmpty(final Collection coll) { return coll == null || coll.isEmpty(); } /** * Null-safe check if the specified collection is not empty. *

* Null returns false. * * @param coll the collection to check, may be null * @return true if non-null and non-empty * @since 3.2 */ public static boolean isNotEmpty(final Collection coll) { return !isEmpty(coll); } //----------------------------------------------------------------------- /** * Reverses the order of the given array. * * @param array the array to reverse */ public static void reverseArray(final Object[] array) { int i = 0; int j = array.length - 1; Object tmp; while (j > i) { tmp = array[j]; array[j] = array[i]; array[i] = tmp; j--; i++; } } /** * Returns true if no more elements can be added to the Collection. *

* This method uses the {@link BoundedCollection} interface to determine the * full status. If the collection does not implement this interface then * false is returned. *

* The collection does not have to implement this interface directly. * If the collection has been decorated using the decorators subpackage * then these will be removed to access the BoundedCollection. * * @param coll the collection to check * @return true if the BoundedCollection is full * @throws NullPointerException if the collection is null */ public static boolean isFull(final Collection coll) { if (coll == null) { throw new NullPointerException("The collection must not be null"); } if (coll instanceof BoundedCollection) { return ((BoundedCollection) coll).isFull(); } try { final BoundedCollection bcoll = UnmodifiableBoundedCollection.unmodifiableBoundedCollection(coll); return bcoll.isFull(); } catch (final IllegalArgumentException ex) { return false; } } /** * Get the maximum number of elements that the Collection can contain. *

* This method uses the {@link BoundedCollection} interface to determine the * maximum size. If the collection does not implement this interface then * -1 is returned. *

* The collection does not have to implement this interface directly. * If the collection has been decorated using the decorators subpackage * then these will be removed to access the BoundedCollection. * * @param coll the collection to check * @return the maximum size of the BoundedCollection, -1 if no maximum size * @throws NullPointerException if the collection is null */ public static int maxSize(final Collection coll) { if (coll == null) { throw new NullPointerException("The collection must not be null"); } if (coll instanceof BoundedCollection) { return ((BoundedCollection) coll).maxSize(); } try { final BoundedCollection bcoll = UnmodifiableBoundedCollection.unmodifiableBoundedCollection(coll); return bcoll.maxSize(); } catch (final IllegalArgumentException ex) { return -1; } } //----------------------------------------------------------------------- /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the natural ordering of the elements is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection is null * @since 4.0 */ public static > List collate(Iterable a, Iterable b) { return collate(a, b, ComparatorUtils.naturalComparator(), true); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the natural ordering of the elements is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param includeDuplicates if {@code true} duplicate elements will be retained, otherwise * they will be removed in the output collection * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection is null * @since 4.0 */ public static > List collate(final Iterable a, final Iterable b, final boolean includeDuplicates) { return collate(a, b, ComparatorUtils.naturalComparator(), includeDuplicates); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the ordering of the elements according to Comparator c is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param c the comparator to use for the merge. * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection or the comparator is null * @since 4.0 */ public static List collate(final Iterable a, final Iterable b, final Comparator c) { return collate(a, b, c, true); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the ordering of the elements according to Comparator c is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param c the comparator to use for the merge. * @param includeDuplicates if {@code true} duplicate elements will be retained, otherwise * they will be removed in the output collection * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection or the comparator is null * @since 4.0 */ public static List collate(final Iterable a, final Iterable b, final Comparator c, final boolean includeDuplicates) { if (a == null || b == null) { throw new IllegalArgumentException("The collections must not be null"); } if (c == null) { throw new IllegalArgumentException("The comparator must not be null"); } // if both Iterables are a Collection, we can estimate the size final int totalSize = a instanceof Collection && b instanceof Collection ? Math.max(1, ((Collection) a).size() + ((Collection) b).size()) : 10; final Iterator iterator = new CollatingIterator(c, a.iterator(), b.iterator()); if (includeDuplicates) { return IteratorUtils.toList(iterator, totalSize); } else { final ArrayList mergedList = new ArrayList(totalSize); O lastItem = null; while (iterator.hasNext()) { final O item = iterator.next(); if (lastItem == null || !lastItem.equals(item)) { mergedList.add(item); } lastItem = item; } mergedList.trimToSize(); return mergedList; } } //----------------------------------------------------------------------- /** * Returns a {@link Collection} of all the permutations of the input collection. *

* NOTE: the number of permutations of a given collection is equal to n!, where * n is the size of the collection. Thus, the resulting collection will become * very large for collections > 10 (e.g. 10! = 3628800, 15! = 1307674368000). *

* For larger collections it is advised to use a {@link PermutationIterator} to * iterate over all permutations. * * @see PermutationIterator * * @param the element type * @param collection the collection to create permutations for, may not be null * @return an unordered collection of all permutations of the input collection * @throws NullPointerException if collection is null * @since 4.0 */ public static Collection> permutations(final Collection collection) { final PermutationIterator it = new PermutationIterator(collection); final Collection> result = new LinkedList>(); while (it.hasNext()) { result.add(it.next()); } return result; } //----------------------------------------------------------------------- /** * Returns a collection containing all the elements in collection * that are also in retain. The cardinality of an element e * in the returned collection is the same as the cardinality of e * in collection unless retain does not contain e, in which * case the cardinality is zero. This method is useful if you do not wish to modify * the collection c and thus cannot call c.retainAll(retain);. * * @param the type of object the {@link Collection} contains * @param collection the collection whose contents are the target of the #retailAll operation * @param retain the collection containing the elements to be retained in the returned collection * @return a Collection containing all the elements of collection * that occur at least once in retain. * @throws NullPointerException if either parameter is null * @since 3.2 */ public static Collection retainAll(final Collection collection, final Collection retain) { return ListUtils.retainAll(collection, retain); } /** * Removes the elements in remove from collection. That is, this * method returns a collection containing all the elements in c * that are not in remove. The cardinality of an element e * in the returned collection is the same as the cardinality of e * in collection unless remove contains e, in which * case the cardinality is zero. This method is useful if you do not wish to modify * the collection c and thus cannot call collection.removeAll(remove);. * * @param the type of object the {@link Collection} contains * @param collection the collection from which items are removed (in the returned collection) * @param remove the items to be removed from the returned collection * @return a Collection containing all the elements of collection except * any elements that also occur in remove. * @throws NullPointerException if either parameter is null * @since 4.0 (method existed in 3.2 but was completely broken) */ public static Collection removeAll(final Collection collection, final Collection remove) { return ListUtils.removeAll(collection, remove); } //----------------------------------------------------------------------- /** * Returns a synchronized collection backed by the given collection. *

* You must manually synchronize on the returned buffer's iterator to * avoid non-deterministic behavior: * *

     * Collection c = CollectionUtils.synchronizedCollection(myCollection);
     * synchronized (c) {
     *     Iterator i = c.iterator();
     *     while (i.hasNext()) {
     *         process (i.next());
     *     }
     * }
     * 
* * This method uses the implementation in the decorators subpackage. * * @param the type of object the {@link Collection} contains * @param collection the collection to synchronize, must not be null * @return a synchronized collection backed by the given collection * @throws IllegalArgumentException if the collection is null */ public static Collection synchronizedCollection(final Collection collection) { return SynchronizedCollection.synchronizedCollection(collection); } /** * Returns an unmodifiable collection backed by the given collection. *

* This method uses the implementation in the decorators subpackage. * * @param the type of object the {@link Collection} contains * @param collection the collection to make unmodifiable, must not be null * @return an unmodifiable collection backed by the given collection * @throws IllegalArgumentException if the collection is null */ public static Collection unmodifiableCollection(final Collection collection) { return UnmodifiableCollection.unmodifiableCollection(collection); } /** * Returns a predicated (validating) collection backed by the given collection. *

* Only objects that pass the test in the given predicate can be added to the collection. * Trying to add an invalid object results in an IllegalArgumentException. * It is important not to use the original collection after invoking this method, * as it is a backdoor for adding invalid objects. * * @param collection the collection to predicate, must not be null * @param predicate the predicate for the collection, must not be null * @param the type of objects in the Collection. * @return a predicated collection backed by the given collection * @throws IllegalArgumentException if the Collection is null */ public static Collection predicatedCollection(final Collection collection, final Predicate predicate) { return PredicatedCollection.predicatedCollection(collection, predicate); } /** * Returns a transformed bag backed by the given collection. *

* Each object is passed through the transformer as it is added to the * Collection. It is important not to use the original collection after invoking this * method, as it is a backdoor for adding untransformed objects. *

* Existing entries in the specified collection will not be transformed. * If you want that behaviour, see {@link TransformedCollection#transformedCollection}. * * @param the type of object the {@link Collection} contains * @param collection the collection to predicate, must not be null * @param transformer the transformer for the collection, must not be null * @return a transformed collection backed by the given collection * @throws IllegalArgumentException if the Collection or Transformer is null */ public static Collection transformingCollection(final Collection collection, final Transformer transformer) { return TransformedCollection.transformingCollection(collection, transformer); } /** * Extract the lone element of the specified Collection. * @param collection type * @param collection to read * @return sole member of collection * @throws IllegalArgumentException if collection is null/empty or contains more than one element * @since 4.0 */ public static E extractSingleton(final Collection collection) { if (collection == null || collection.size() != 1) { throw new IllegalArgumentException("Can extract singleton only when collection size == 1"); } return collection.iterator().next(); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 7424; to = 7341.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.bag.HashBag; import org.apache.commons.collections4.collection.PredicatedCollection; import org.apache.commons.collections4.collection.SynchronizedCollection; import org.apache.commons.collections4.collection.TransformedCollection; import org.apache.commons.collections4.collection.UnmodifiableBoundedCollection; import org.apache.commons.collections4.collection.UnmodifiableCollection; import org.apache.commons.collections4.functors.TruePredicate; import org.apache.commons.collections4.iterators.CollatingIterator; import org.apache.commons.collections4.iterators.PermutationIterator; /** * Provides utility methods and decorators for {@link Collection} instances. *

* NOTE: From 4.0, method parameters will take {@link Iterable} objects when possible. * * @since 1.0 * @version $Id: CollectionUtils.java 1540639 2013-11-11 08:54:12Z tn $ */ public class CollectionUtils { /** * Helper class to easily access cardinality properties of two collections. * @param the element type */ private static class CardinalityHelper { /** Contains the cardinality for each object in collection A. */ final Map cardinalityA; /** Contains the cardinality for each object in collection B. */ final Map cardinalityB; /** * Create a new CardinalityHelper for two collections. * @param a the first collection * @param b the second collection */ public CardinalityHelper(final Iterable a, final Iterable b) { cardinalityA = CollectionUtils.getCardinalityMap(a); cardinalityB = CollectionUtils.getCardinalityMap(b); } /** * Returns the maximum frequency of an object. * @param obj the object * @return the maximum frequency of the object */ public final int max(final Object obj) { return Math.max(freqA(obj), freqB(obj)); } /** * Returns the minimum frequency of an object. * @param obj the object * @return the minimum frequency of the object */ public final int min(final Object obj) { return Math.min(freqA(obj), freqB(obj)); } /** * Returns the frequency of this object in collection A. * @param obj the object * @return the frequency of the object in collection A */ public int freqA(final Object obj) { return getFreq(obj, cardinalityA); } /** * Returns the frequency of this object in collection B. * @param obj the object * @return the frequency of the object in collection B */ public int freqB(final Object obj) { return getFreq(obj, cardinalityB); } private final int getFreq(final Object obj, final Map freqMap) { final Integer count = freqMap.get(obj); if (count != null) { return count.intValue(); } return 0; } } /** * Helper class for set-related operations, e.g. union, subtract, intersection. * @param the element type */ private static class SetOperationCardinalityHelper extends CardinalityHelper implements Iterable { /** Contains the unique elements of the two collections. */ private final Set elements; /** Output collection. */ private final List newList; /** * Create a new set operation helper from the two collections. * @param a the first collection * @param b the second collection */ public SetOperationCardinalityHelper(final Iterable a, final Iterable b) { super(a, b); elements = new HashSet(); addAll(elements, a); addAll(elements, b); // the resulting list must contain at least each unique element, but may grow newList = new ArrayList(elements.size()); } public Iterator iterator() { return elements.iterator(); } /** * Add the object {@code count} times to the result collection. * @param obj the object to add * @param count the count */ public void setCardinality(final O obj, final int count) { for (int i = 0; i < count; i++) { newList.add(obj); } } /** * Returns the resulting collection. * @return the result */ public Collection list() { return newList; } } /** * An empty unmodifiable collection. * The JDK provides empty Set and List implementations which could be used for * this purpose. However they could be cast to Set or List which might be * undesirable. This implementation only implements Collection. */ @SuppressWarnings("rawtypes") // we deliberately use the raw type here public static final Collection EMPTY_COLLECTION = UnmodifiableCollection.unmodifiableCollection(new ArrayList()); /** * CollectionUtils should not normally be instantiated. */ private CollectionUtils() {} /** * Returns the immutable EMPTY_COLLECTION with generic type safety. * * @see #EMPTY_COLLECTION * @since 4.0 * @param the element type * @return immutable empty collection */ @SuppressWarnings("unchecked") // OK, empty collection is compatible with any type public static Collection emptyCollection() { return EMPTY_COLLECTION; } /** * Returns an immutable empty collection if the argument is null, * or the argument itself otherwise. * * @param the element type * @param collection the collection, possibly null * @return an empty collection if the argument is null */ @SuppressWarnings("unchecked") // OK, empty collection is compatible with any type public static Collection emptyIfNull(final Collection collection) { return collection == null ? EMPTY_COLLECTION : collection; } /** * Returns a {@link Collection} containing the union of the given * {@link Iterable}s. *

* The cardinality of each element in the returned {@link Collection} will * be equal to the maximum of the cardinality of that element in the two * given {@link Iterable}s. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the union of the two collections * @see Collection#addAll */ public static Collection union(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.max(obj)); } return helper.list(); } /** * Returns a {@link Collection} containing the intersection of the given * {@link Iterable}s. *

* The cardinality of each element in the returned {@link Collection} will * be equal to the minimum of the cardinality of that element in the two * given {@link Iterable}s. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the intersection of the two collections * @see Collection#retainAll * @see #containsAny */ public static Collection intersection(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.min(obj)); } return helper.list(); } /** * Returns a {@link Collection} containing the exclusive disjunction * (symmetric difference) of the given {@link Iterable}s. *

* The cardinality of each element e in the returned * {@link Collection} will be equal to * max(cardinality(e,a),cardinality(e,b)) - min(cardinality(e,a), * cardinality(e,b)). *

* This is equivalent to * {@link #subtract subtract}({@link #union union(a,b)},{@link #intersection intersection(a,b)}) * or * {@link #union union}({@link #subtract subtract(a,b)},{@link #subtract subtract(b,a)}). * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the symmetric difference of the two collections */ public static Collection disjunction(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.max(obj) - helper.min(obj)); } return helper.list(); } /** * Returns a new {@link Collection} containing a - b. * The cardinality of each element e in the returned {@link Collection} * will be the cardinality of e in a minus the cardinality * of e in b, or zero, whichever is greater. * * @param a the collection to subtract from, must not be null * @param b the collection to subtract, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return a new collection with the results * @see Collection#removeAll */ public static Collection subtract(final Iterable a, final Iterable b) { final Predicate p = TruePredicate.truePredicate(); return subtract(a, b, p); } /** * Returns a new {@link Collection} containing a minus a subset of * b. Only the elements of b that satisfy the predicate * condition, p are subtracted from a. * *

The cardinality of each element e in the returned {@link Collection} * that satisfies the predicate condition will be the cardinality of e in a * minus the cardinality of e in b, or zero, whichever is greater.

*

The cardinality of each element e in the returned {@link Collection} that does not * satisfy the predicate condition will be equal to the cardinality of e in a.

* * @param a the collection to subtract from, must not be null * @param b the collection to subtract, must not be null * @param p the condition used to determine which elements of b are * subtracted. * @param the generic type that is able to represent the types contained * in both input collections. * @return a new collection with the results * @since 4.0 * @see Collection#removeAll */ public static Collection subtract(final Iterable a, final Iterable b, final Predicate p) { final ArrayList list = new ArrayList(); final HashBag bag = new HashBag(); for (final O element : b) { if (p.evaluate(element)) { bag.add(element); } } for (final O element : a) { if (!bag.remove(element, 1)) { list.add(element); } } return list; } /** * Returns true iff all elements of {@code coll2} are also contained * in {@code coll1}. The cardinality of values in {@code coll2} is not taken into account, * which is the same behavior as {@link Collection#containsAll(Collection)}. *

* In other words, this method returns true iff the * {@link #intersection} of coll1 and coll2 has the same cardinality as * the set of unique values from {@code coll2}. In case {@code coll2} is empty, {@code true} * will be returned. *

* This method is intended as a replacement for {@link Collection#containsAll(Collection)} * with a guaranteed runtime complexity of {@code O(n + m)}. Depending on the type of * {@link Collection} provided, this method will be much faster than calling * {@link Collection#containsAll(Collection)} instead, though this will come at the * cost of an additional space complexity O(n). * * @param coll1 the first collection, must not be null * @param coll2 the second collection, must not be null * @return true iff the intersection of the collections has the same cardinality * as the set of unique elements from the second collection * @since 4.0 */ public static boolean containsAll(final Collection coll1, final Collection coll2) { if (coll2.isEmpty()) { return true; } else { final Iterator it = coll1.iterator(); final Set elementsAlreadySeen = new HashSet(); for (final Object nextElement : coll2) { if (elementsAlreadySeen.contains(nextElement)) { continue; } boolean foundCurrentElement = false; while (it.hasNext()) { final Object p = it.next(); elementsAlreadySeen.add(p); if (nextElement == null ? p == null : nextElement.equals(p)) { foundCurrentElement = true; break; } } if (foundCurrentElement) { continue; } else { return false; } } return true; } } /** * Returns true iff at least one element is in both collections. *

* In other words, this method returns true iff the * {@link #intersection} of coll1 and coll2 is not empty. * * @param coll1 the first collection, must not be null * @param coll2 the second collection, must not be null * @return true iff the intersection of the collections is non-empty * @since 2.1 * @see #intersection */ public static boolean containsAny(final Collection coll1, final Collection coll2) { if (coll1.size() < coll2.size()) { for (final Object aColl1 : coll1) { if (coll2.contains(aColl1)) { return true; } } } else { for (final Object aColl2 : coll2) { if (coll1.contains(aColl2)) { return true; } } } return false; } /** * Returns a {@link Map} mapping each unique element in the given * {@link Collection} to an {@link Integer} representing the number * of occurrences of that element in the {@link Collection}. *

* Only those elements present in the collection will appear as * keys in the map. * * @param the type of object in the returned {@link Map}. This is a super type of . * @param coll the collection to get the cardinality map for, must not be null * @return the populated cardinality map */ public static Map getCardinalityMap(final Iterable coll) { final Map count = new HashMap(); for (final O obj : coll) { final Integer c = count.get(obj); if (c == null) { count.put(obj, Integer.valueOf(1)); } else { count.put(obj, Integer.valueOf(c.intValue() + 1)); } } return count; } /** * Returns true iff a is a sub-collection of b, * that is, iff the cardinality of e in a is less than or * equal to the cardinality of e in b, for each element e * in a. * * @param a the first (sub?) collection, must not be null * @param b the second (super?) collection, must not be null * @return true iff a is a sub-collection of b * @see #isProperSubCollection * @see Collection#containsAll */ public static boolean isSubCollection(final Collection a, final Collection b) { final CardinalityHelper helper = new CardinalityHelper(a, b); for (final Object obj : a) { if (helper.freqA(obj) > helper.freqB(obj)) { return false; } } return true; } /** * Returns true iff a is a proper sub-collection of b, * that is, iff the cardinality of e in a is less * than or equal to the cardinality of e in b, * for each element e in a, and there is at least one * element f such that the cardinality of f in b * is strictly greater than the cardinality of f in a. *

* The implementation assumes *

    *
  • a.size() and b.size() represent the * total cardinality of a and b, resp.
  • *
  • a.size() < Integer.MAXVALUE
  • *
* * @param a the first (sub?) collection, must not be null * @param b the second (super?) collection, must not be null * @return true iff a is a proper sub-collection of b * @see #isSubCollection * @see Collection#containsAll */ public static boolean isProperSubCollection(final Collection a, final Collection b) { return a.size() < b.size() && CollectionUtils.isSubCollection(a, b); } /** * Returns true iff the given {@link Collection}s contain * exactly the same elements with exactly the same cardinalities. *

* That is, iff the cardinality of e in a is * equal to the cardinality of e in b, * for each element e in a or b. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @return true iff the collections contain the same elements with the same cardinalities. */ public static boolean isEqualCollection(final Collection a, final Collection b) { if(a.size() != b.size()) { return false; } final CardinalityHelper helper = new CardinalityHelper(a, b); if(helper.cardinalityA.size() != helper.cardinalityB.size()) { return false; } for( final Object obj : helper.cardinalityA.keySet()) { if(helper.freqA(obj) != helper.freqB(obj)) { return false; } } return true; } /** * Returns true iff the given {@link Collection}s contain * exactly the same elements with exactly the same cardinalities. *

* That is, iff the cardinality of e in a is * equal to the cardinality of e in b, * for each element e in a or b. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param equator the Equator used for testing equality * @return true iff the collections contain the same elements with the same cardinalities. * @throws IllegalArgumentException if the equator is null * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) // we don't know the types due to wildcards in the signature public static boolean isEqualCollection(final Collection a, final Collection b, final Equator equator) { if (equator == null) { throw new IllegalArgumentException("equator may not be null"); } if(a.size() != b.size()) { return false; } final Transformer transformer = new Transformer() { public EquatorWrapper transform(final Object input) { return new EquatorWrapper(equator, input); } }; return isEqualCollection(collect(a, transformer), collect(b, transformer)); } /** * Wraps another object and uses the provided Equator to implement * {@link #equals(Object)} and {@link #hashCode()}. *

* This class can be used to store objects into a Map. * * @param the element type * @since 4.0 */ private static class EquatorWrapper { private final Equator equator; private final O object; public EquatorWrapper(final Equator equator, final O object) { this.equator = equator; this.object = object; } public O getObject() { return object; } @Override public boolean equals(final Object obj) { if (!(obj instanceof EquatorWrapper)) { return false; } @SuppressWarnings("unchecked") final EquatorWrapper otherObj = (EquatorWrapper) obj; return equator.equate(object, otherObj.getObject()); } @Override public int hashCode() { return equator.hash(object); } } /** * Returns the number of occurrences of obj in coll. * * @param obj the object to find the cardinality of * @param coll the {@link Iterable} to search * @param the type of object that the {@link Iterable} may contain. * @return the the number of occurrences of obj in coll */ public static int cardinality(final O obj, final Iterable coll) { if (coll instanceof Set) { return ((Set) coll).contains(obj) ? 1 : 0; } if (coll instanceof Bag) { return ((Bag) coll).getCount(obj); } int count = 0; if (obj == null) { for (final Object element : coll) { if (element == null) { count++; } } } else { for (final Object element : coll) { if (obj.equals(element)) { count++; } } } return count; } /** * Finds the first element in the given collection which matches the given predicate. *

* If the input collection or predicate is null, or no element of the collection * matches the predicate, null is returned. * * @param the type of object the {@link Iterable} contains * @param collection the collection to search, may be null * @param predicate the predicate to use, may be null * @return the first element of the collection which matches the predicate or null if none could be found */ public static T find(final Iterable collection, final Predicate predicate) { if (collection != null && predicate != null) { for (final T item : collection) { if (predicate.evaluate(item)) { return item; } } } return null; } /** * Executes the given closure on each element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param the closure type * @param collection the collection to get the input from, may be null * @param closure the closure to perform, may be null * @return closure */ public static > C forAllDo(final Iterable collection, final C closure) { if (collection != null && closure != null) { for (final T element : collection) { closure.execute(element); } } return closure; } /** * Executes the given closure on each element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterator} contains * @param the closure type * @param iterator the iterator to get the input from, may be null * @param closure the closure to perform, may be null * @return closure * @since 4.0 */ public static > C forAllDo(final Iterator iterator, final C closure) { if (iterator != null && closure != null) { while (iterator.hasNext()) { closure.execute(iterator.next()); } } return closure; } /** * Executes the given closure on each but the last element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param the closure type * @param collection the collection to get the input from, may be null * @param closure the closure to perform, may be null * @return the last element in the collection, or null if either collection or closure is null * @since 4.0 */ public static > T forAllButLastDo(final Iterable collection, final C closure) { return collection != null && closure != null ? forAllButLastDo(collection.iterator(), closure) : null; } /** * Executes the given closure on each but the last element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Collection} contains * @param the closure type * @param iterator the iterator to get the input from, may be null * @param closure the closure to perform, may be null * @return the last element in the collection, or null if either iterator or closure is null * @since 4.0 */ public static > T forAllButLastDo(final Iterator iterator, final C closure) { if (iterator != null && closure != null) { while (iterator.hasNext()) { final T element = iterator.next(); if (iterator.hasNext()) { closure.execute(element); } else { return element; } } } return null; } /** * Filter the collection by applying a Predicate to each element. If the * predicate returns false, remove the element. *

* If the input collection or predicate is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param collection the collection to get the input from, may be null * @param predicate the predicate to use as a filter, may be null * @return true if the collection is modified by this call, false otherwise. */ public static boolean filter(final Iterable collection, final Predicate predicate) { boolean result = false; if (collection != null && predicate != null) { for (final Iterator it = collection.iterator(); it.hasNext();) { if (!predicate.evaluate(it.next())) { it.remove(); result = true; } } } return result; } /** * Filter the collection by applying a Predicate to each element. If the * predicate returns true, remove the element. *

* This is equivalent to

filter(collection, PredicateUtils.notPredicate(predicate))
* if predicate is != null. *

* If the input collection or predicate is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param collection the collection to get the input from, may be null * @param predicate the predicate to use as a filter, may be null * @return true if the collection is modified by this call, false otherwise. */ public static boolean filterInverse(final Iterable collection, final Predicate predicate) { return filter(collection, predicate == null ? null : PredicateUtils.notPredicate(predicate)); } /** * Transform the collection by applying a Transformer to each element. *

* If the input collection or transformer is null, there is no change made. *

* This routine is best for Lists, for which set() is used to do the * transformations "in place." For other Collections, clear() and addAll() * are used to replace elements. *

* If the input collection controls its input, such as a Set, and the * Transformer creates duplicates (or are otherwise invalid), the collection * may reduce in size due to calling this method. * * @param the type of object the {@link Collection} contains * @param collection the {@link Collection} to get the input from, may be null * @param transformer the transformer to perform, may be null */ public static void transform(final Collection collection, final Transformer transformer) { if (collection != null && transformer != null) { if (collection instanceof List) { final List list = (List) collection; for (final ListIterator it = list.listIterator(); it.hasNext();) { it.set(transformer.transform(it.next())); } } else { final Collection resultCollection = collect(collection, transformer); collection.clear(); collection.addAll(resultCollection); } } } /** * Counts the number of elements in the input collection that match the * predicate. *

* A null collection or predicate matches no elements. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return the number of matches for the predicate in the collection */ public static int countMatches(final Iterable input, final Predicate predicate) { int count = 0; if (input != null && predicate != null) { for (final C o : input) { if (predicate.evaluate(o)) { count++; } } } return count; } /** * Answers true if a predicate is true for at least one element of a * collection. *

* A null collection or predicate returns false. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return true if at least one element of the collection matches the predicate */ public static boolean exists(final Iterable input, final Predicate predicate) { if (input != null && predicate != null) { for (final C o : input) { if (predicate.evaluate(o)) { return true; } } } return false; } /** * Answers true if a predicate is true for every element of a * collection. *

* A null predicate returns false.
* A null or empty collection returns true. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return true if every element of the collection matches the predicate or if the * collection is empty, false otherwise * @since 4.0 */ public static boolean matchesAll(final Iterable input, final Predicate predicate) { if (predicate == null) { return false; } if (input != null) { for (final C o : input) { if (!predicate.evaluate(o)) { return false; } } } return true; } /** * Selects all elements from input collection which match the given * predicate into an output collection. *

* A null predicate matches no elements. * * @param the type of object the {@link Iterable} contains * @param inputCollection the collection to get the input from, may not be null * @param predicate the predicate to use, may be null * @return the elements matching the predicate (new list) * @throws NullPointerException if the input collection is null */ public static Collection select(final Iterable inputCollection, final Predicate predicate) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return select(inputCollection, predicate, answer); } /** * Selects all elements from input collection which match the given * predicate and adds them to outputCollection. *

* If the input collection or predicate is null, there is no change to the * output collection. * * @param the type of object the {@link Iterable} contains * @param the type of the output {@link Collection} * @param inputCollection the collection to get the input from, may be null * @param predicate the predicate to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and predicate or not null * @return the outputCollection */ public static > R select(final Iterable inputCollection, final Predicate predicate, final R outputCollection) { if (inputCollection != null && predicate != null) { for (final O item : inputCollection) { if (predicate.evaluate(item)) { outputCollection.add(item); } } } return outputCollection; } /** * Selects all elements from inputCollection which don't match the given * predicate into an output collection. *

* If the input predicate is null, the result is an empty * list. * * @param the type of object the {@link Iterable} contains * @param inputCollection the collection to get the input from, may not be null * @param predicate the predicate to use, may be null * @return the elements not matching the predicate (new list) * @throws NullPointerException if the input collection is null */ public static Collection selectRejected(final Iterable inputCollection, final Predicate predicate) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return selectRejected(inputCollection, predicate, answer); } /** * Selects all elements from inputCollection which don't match the given * predicate and adds them to outputCollection. *

* If the input predicate is null, no elements are added to * outputCollection. * * @param the type of object the {@link Iterable} contains * @param the type of the output {@link Collection} * @param inputCollection the collection to get the input from, may be null * @param predicate the predicate to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and predicate or not null * @return outputCollection */ public static > R selectRejected(final Iterable inputCollection, final Predicate predicate, final R outputCollection) { if (inputCollection != null && predicate != null) { for (final O item : inputCollection) { if (!predicate.evaluate(item)) { outputCollection.add(item); } } } return outputCollection; } /** * Returns a new Collection consisting of the elements of inputCollection * transformed by the given transformer. *

* If the input transformer is null, the result is an empty list. * * @param the type of object in the input collection * @param the type of object in the output collection * @param inputCollection the collection to get the input from, may not be null * @param transformer the transformer to use, may be null * @return the transformed result (new list) * @throws NullPointerException if the input collection is null */ public static Collection collect(final Iterable inputCollection, final Transformer transformer) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return collect(inputCollection, transformer, answer); } /** * Transforms all elements from the inputIterator with the given transformer * and adds them to the outputCollection. *

* If the input iterator or transformer is null, the result is an empty * list. * * @param inputIterator the iterator to get the input from, may be null * @param transformer the transformer to use, may be null * @param the type of object in the input collection * @param the type of object in the output collection * @return the transformed result (new list) */ public static Collection collect(final Iterator inputIterator, final Transformer transformer) { return collect(inputIterator, transformer, new ArrayList()); } /** * Transforms all elements from inputCollection with the given transformer * and adds them to the outputCollection. *

* If the input collection or transformer is null, there is no change to the * output collection. * * @param the type of object in the input collection * @param the type of object in the output collection * @param the output type of the transformer - this extends O. * @param inputCollection the collection to get the input from, may be null * @param transformer the transformer to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and transformer are not null * @return the outputCollection with the transformed input added * @throws NullPointerException if the output collection is null and both, inputCollection and * transformer are not null */ public static > R collect(final Iterable inputCollection, final Transformer transformer, final R outputCollection) { if (inputCollection != null) { return collect(inputCollection.iterator(), transformer, outputCollection); } return outputCollection; } /** * Transforms all elements from the inputIterator with the given transformer * and adds them to the outputCollection. *

* If the input iterator or transformer is null, there is no change to the * output collection. * * @param inputIterator the iterator to get the input from, may be null * @param transformer the transformer to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and transformer are not null * @param the type of object in the input collection * @param the type of object in the output collection * @param the output type of the transformer - this extends O. * @return the outputCollection with the transformed input added * @throws NullPointerException if the output collection is null and both, inputCollection and * transformer are not null */ public static > R collect(final Iterator inputIterator, final Transformer transformer, final R outputCollection) { if (inputIterator != null && transformer != null) { while (inputIterator.hasNext()) { final I item = inputIterator.next(); final O value = transformer.transform(item); outputCollection.add(value); } } return outputCollection; } //----------------------------------------------------------------------- /** * Adds an element to the collection unless the element is null. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param object the object to add, if null it will not be added * @return true if the collection changed * @throws NullPointerException if the collection is null * @since 3.2 */ public static boolean addIgnoreNull(final Collection collection, final T object) { if (collection == null) { throw new NullPointerException("The collection must not be null"); } return object != null && collection.add(object); } /** * Adds all elements in the {@link Iterable} to the given collection. If the * {@link Iterable} is a {@link Collection} then it is cast and will be * added using {@link Collection#addAll(Collection)} instead of iterating. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param iterable the iterable of elements to add, must not be null * @return a boolean indicating whether the collection has changed or not. * @throws NullPointerException if the collection or iterator is null */ public static boolean addAll(final Collection collection, final Iterable iterable) { if (iterable instanceof Collection) { return collection.addAll((Collection) iterable); } return addAll(collection, iterable.iterator()); } /** * Adds all elements in the iteration to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param iterator the iterator of elements to add, must not be null * @return a boolean indicating whether the collection has changed or not. * @throws NullPointerException if the collection or iterator is null */ public static boolean addAll(final Collection collection, final Iterator iterator) { boolean changed = false; while (iterator.hasNext()) { changed |= collection.add(iterator.next()); } return changed; } /** * Adds all elements in the enumeration to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param enumeration the enumeration of elements to add, must not be null * @return {@code true} if the collections was changed, {@code false} otherwise * @throws NullPointerException if the collection or enumeration is null */ public static boolean addAll(final Collection collection, final Enumeration enumeration) { boolean changed = false; while (enumeration.hasMoreElements()) { changed |= collection.add(enumeration.nextElement()); } return changed; } /** * Adds all elements in the array to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param elements the array of elements to add, must not be null * @return {@code true} if the collection was changed, {@code false} otherwise * @throws NullPointerException if the collection or array is null */ public static boolean addAll(final Collection collection, final C[] elements) { boolean changed = false; for (final C element : elements) { changed |= collection.add(element); } return changed; } /** * Returns the index-th value in {@link Iterator}, throwing * IndexOutOfBoundsException if there is no such element. *

* The Iterator is advanced to index (or to the end, if * index exceeds the number of entries) as a side effect of this method. * * @param iterator the iterator to get a value from * @param index the index to get * @param the type of object in the {@link Iterator} * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid * @throws IllegalArgumentException if the object type is invalid */ public static T get(final Iterator iterator, final int index) { int i = index; checkIndexBounds(i); while (iterator.hasNext()) { i--; if (i == -1) { return iterator.next(); } iterator.next(); } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } /** * Ensures an index is not negative. * @param index the index to check. * @throws IndexOutOfBoundsException if the index is negative. */ private static void checkIndexBounds(final int index) { if (index < 0) { throw new IndexOutOfBoundsException("Index cannot be negative: " + index); } } /** * Returns the index-th value in the iterable's {@link Iterator}, throwing * IndexOutOfBoundsException if there is no such element. *

* If the {@link Iterable} is a {@link List}, then it will use {@link List#get(int)}. * * @param iterable the {@link Iterable} to get a value from * @param index the index to get * @param the type of object in the {@link Iterable}. * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid */ public static T get(final Iterable iterable, final int index) { checkIndexBounds(index); if (iterable instanceof List) { return ((List) iterable).get(index); } return get(iterable.iterator(), index); } /** * Returns the index-th value in object, throwing * IndexOutOfBoundsException if there is no such element or * IllegalArgumentException if object is not an * instance of one of the supported types. *

* The supported types, and associated semantics are: *

    *
  • Map -- the value returned is the Map.Entry in position * index in the map's entrySet iterator, * if there is such an entry.
  • *
  • List -- this method is equivalent to the list's get method.
  • *
  • Array -- the index-th array entry is returned, * if there is such an entry; otherwise an IndexOutOfBoundsException * is thrown.
  • *
  • Collection -- the value returned is the index-th object * returned by the collection's default iterator, if there is such an element.
  • *
  • Iterator or Enumeration -- the value returned is the * index-th object in the Iterator/Enumeration, if there * is such an element. The Iterator/Enumeration is advanced to * index (or to the end, if index exceeds the * number of entries) as a side effect of this method.
  • *
* * @param object the object to get a value from * @param index the index to get * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid * @throws IllegalArgumentException if the object type is invalid */ public static Object get(final Object object, final int index) { int i = index; if (i < 0) { throw new IndexOutOfBoundsException("Index cannot be negative: " + i); } if (object instanceof Map) { final Map map = (Map) object; final Iterator iterator = map.entrySet().iterator(); return get(iterator, i); } else if (object instanceof Object[]) { return ((Object[]) object)[i]; } else if (object instanceof Iterator) { final Iterator it = (Iterator) object; while (it.hasNext()) { i--; if (i == -1) { return it.next(); } it.next(); } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } else if (object instanceof Collection) { final Iterator iterator = ((Collection) object).iterator(); return get(iterator, i); } else if (object instanceof Enumeration) { final Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { i--; if (i == -1) { return it.nextElement(); } else { it.nextElement(); } } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } else if (object == null) { throw new IllegalArgumentException("Unsupported object type: null"); } else { try { return Array.get(object, i); } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } /** * Returns the index-th Map.Entry in the map's entrySet, * throwing IndexOutOfBoundsException if there is no such element. * * @param the key type in the {@link Map} * @param the key type in the {@link Map} * @param map the object to get a value from * @param index the index to get * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid */ public static Map.Entry get(final Map map, final int index) { checkIndexBounds(index); return get(map.entrySet(), index); } /** * Gets the size of the collection/iterator specified. *

* This method can handles objects as follows *

    *
  • Collection - the collection size *
  • Map - the map size *
  • Array - the array size *
  • Iterator - the number of elements remaining in the iterator *
  • Enumeration - the number of elements remaining in the enumeration *
* * @param object the object to get the size of, may be null * @return the size of the specified collection or 0 if the object was null * @throws IllegalArgumentException thrown if object is not recognised * @since 3.1 */ public static int size(final Object object) { if (object == null) { return 0; } int total = 0; if (object instanceof Map) { total = ((Map) object).size(); } else if (object instanceof Collection) { total = ((Collection) object).size(); } else if (object instanceof Object[]) { total = ((Object[]) object).length; } else if (object instanceof Iterator) { final Iterator it = (Iterator) object; while (it.hasNext()) { total++; it.next(); } } else if (object instanceof Enumeration) { final Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { total++; it.nextElement(); } } else { try { total = Array.getLength(object); } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } return total; } /** * Checks if the specified collection/array/iterator is empty. *

* This method can handles objects as follows *

    *
  • Collection - via collection isEmpty *
  • Map - via map isEmpty *
  • Array - using array size *
  • Iterator - via hasNext *
  • Enumeration - via hasMoreElements *
*

* Note: This method is named to avoid clashing with * {@link #isEmpty(Collection)}. * * @param object the object to get the size of, may be null * @return true if empty or null * @throws IllegalArgumentException thrown if object is not recognised * @since 3.2 */ public static boolean sizeIsEmpty(final Object object) { if (object == null) { return true; } else if (object instanceof Collection) { return ((Collection) object).isEmpty(); } else if (object instanceof Map) { return ((Map) object).isEmpty(); } else if (object instanceof Object[]) { return ((Object[]) object).length == 0; } else if (object instanceof Iterator) { return ((Iterator) object).hasNext() == false; } else if (object instanceof Enumeration) { return ((Enumeration) object).hasMoreElements() == false; } else { try { return Array.getLength(object) == 0; } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } //----------------------------------------------------------------------- /** * Null-safe check if the specified collection is empty. *

* Null returns true. * * @param coll the collection to check, may be null * @return true if empty or null * @since 3.2 */ public static boolean isEmpty(final Collection coll) { return coll == null || coll.isEmpty(); } /** * Null-safe check if the specified collection is not empty. *

* Null returns false. * * @param coll the collection to check, may be null * @return true if non-null and non-empty * @since 3.2 */ public static boolean isNotEmpty(final Collection coll) { return !isEmpty(coll); } //----------------------------------------------------------------------- /** * Reverses the order of the given array. * * @param array the array to reverse */ public static void reverseArray(final Object[] array) { int i = 0; int j = array.length - 1; Object tmp; while (j > i) { tmp = array[j]; array[j] = array[i]; array[i] = tmp; j--; i++; } } /** * Returns true if no more elements can be added to the Collection. *

* This method uses the {@link BoundedCollection} interface to determine the * full status. If the collection does not implement this interface then * false is returned. *

* The collection does not have to implement this interface directly. * If the collection has been decorated using the decorators subpackage * then these will be removed to access the BoundedCollection. * * @param coll the collection to check * @return true if the BoundedCollection is full * @throws NullPointerException if the collection is null */ public static boolean isFull(final Collection coll) { if (coll == null) { throw new NullPointerException("The collection must not be null"); } if (coll instanceof BoundedCollection) { return ((BoundedCollection) coll).isFull(); } try { final BoundedCollection bcoll = UnmodifiableBoundedCollection.unmodifiableBoundedCollection(coll); return bcoll.isFull(); } catch (final IllegalArgumentException ex) { return false; } } /** * Get the maximum number of elements that the Collection can contain. *

* This method uses the {@link BoundedCollection} interface to determine the * maximum size. If the collection does not implement this interface then * -1 is returned. *

* The collection does not have to implement this interface directly. * If the collection has been decorated using the decorators subpackage * then these will be removed to access the BoundedCollection. * * @param coll the collection to check * @return the maximum size of the BoundedCollection, -1 if no maximum size * @throws NullPointerException if the collection is null */ public static int maxSize(final Collection coll) { if (coll == null) { throw new NullPointerException("The collection must not be null"); } if (coll instanceof BoundedCollection) { return ((BoundedCollection) coll).maxSize(); } try { final BoundedCollection bcoll = UnmodifiableBoundedCollection.unmodifiableBoundedCollection(coll); return bcoll.maxSize(); } catch (final IllegalArgumentException ex) { return -1; } } //----------------------------------------------------------------------- /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the natural ordering of the elements is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection is null * @since 4.0 */ public static > List collate(Iterable a, Iterable b) { return collate(a, b, ComparatorUtils.naturalComparator(), true); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the natural ordering of the elements is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param includeDuplicates if {@code true} duplicate elements will be retained, otherwise * they will be removed in the output collection * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection is null * @since 4.0 */ public static > List collate(final Iterable a, final Iterable b, final boolean includeDuplicates) { return collate(a, b, ComparatorUtils.naturalComparator(), includeDuplicates); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the ordering of the elements according to Comparator c is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param c the comparator to use for the merge. * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection or the comparator is null * @since 4.0 */ public static List collate(final Iterable a, final Iterable b, final Comparator c) { return collate(a, b, c, true); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the ordering of the elements according to Comparator c is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param c the comparator to use for the merge. * @param includeDuplicates if {@code true} duplicate elements will be retained, otherwise * they will be removed in the output collection * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection or the comparator is null * @since 4.0 */ public static List collate(final Iterable a, final Iterable b, final Comparator c, final boolean includeDuplicates) { if (a == null || b == null) { throw new IllegalArgumentException("The collections must not be null"); } if (c == null) { throw new IllegalArgumentException("The comparator must not be null"); } // if both Iterables are a Collection, we can estimate the size final int totalSize = a instanceof Collection && b instanceof Collection ? Math.max(1, ((Collection) a).size() + ((Collection) b).size()) : 10; final Iterator iterator = new CollatingIterator(c, a.iterator(), b.iterator()); if (includeDuplicates) { return IteratorUtils.toList(iterator, totalSize); } else { final ArrayList mergedList = new ArrayList(totalSize); O lastItem = null; while (iterator.hasNext()) { final O item = iterator.next(); if (lastItem == null || !lastItem.equals(item)) { mergedList.add(item); } lastItem = item; } mergedList.trimToSize(); return mergedList; } } //----------------------------------------------------------------------- /** * Returns a {@link Collection} of all the permutations of the input collection. *

* NOTE: the number of permutations of a given collection is equal to n!, where * n is the size of the collection. Thus, the resulting collection will become * very large for collections > 10 (e.g. 10! = 3628800, 15! = 1307674368000). *

* For larger collections it is advised to use a {@link PermutationIterator} to * iterate over all permutations. * * @see PermutationIterator * * @param the element type * @param collection the collection to create permutations for, may not be null * @return an unordered collection of all permutations of the input collection * @throws NullPointerException if collection is null * @since 4.0 */ public static Collection> permutations(final Collection collection) { final PermutationIterator it = new PermutationIterator(collection); final Collection> result = new LinkedList>(); while (it.hasNext()) { result.add(it.next()); } return result; } //----------------------------------------------------------------------- /** * Returns a collection containing all the elements in collection * that are also in retain. The cardinality of an element e * in the returned collection is the same as the cardinality of e * in collection unless retain does not contain e, in which * case the cardinality is zero. This method is useful if you do not wish to modify * the collection c and thus cannot call c.retainAll(retain);. * * @param the type of object the {@link Collection} contains * @param collection the collection whose contents are the target of the #retailAll operation * @param retain the collection containing the elements to be retained in the returned collection * @return a Collection containing all the elements of collection * that occur at least once in retain. * @throws NullPointerException if either parameter is null * @since 3.2 */ public static Collection retainAll(final Collection collection, final Collection retain) { return ListUtils.retainAll(collection, retain); } /** * Removes the elements in remove from collection. That is, this * method returns a collection containing all the elements in c * that are not in remove. The cardinality of an element e * in the returned collection is the same as the cardinality of e * in collection unless remove contains e, in which * case the cardinality is zero. This method is useful if you do not wish to modify * the collection c and thus cannot call collection.removeAll(remove);. * * @param the type of object the {@link Collection} contains * @param collection the collection from which items are removed (in the returned collection) * @param remove the items to be removed from the returned collection * @return a Collection containing all the elements of collection except * any elements that also occur in remove. * @throws NullPointerException if either parameter is null * @since 4.0 (method existed in 3.2 but was completely broken) */ public static Collection removeAll(final Collection collection, final Collection remove) { return ListUtils.removeAll(collection, remove); } //----------------------------------------------------------------------- /** * Returns a synchronized collection backed by the given collection. *

* You must manually synchronize on the returned buffer's iterator to * avoid non-deterministic behavior: * *

     * Collection c = CollectionUtils.synchronizedCollection(myCollection);
     * synchronized (c) {
     *     Iterator i = c.iterator();
     *     while (i.hasNext()) {
     *         process (i.next());
     *     }
     * }
     * 
* * This method uses the implementation in the decorators subpackage. * * @param the type of object the {@link Collection} contains * @param collection the collection to synchronize, must not be null * @return a synchronized collection backed by the given collection * @throws IllegalArgumentException if the collection is null */ public static Collection synchronizedCollection(final Collection collection) { return SynchronizedCollection.synchronizedCollection(collection); } /** * Returns an unmodifiable collection backed by the given collection. *

* This method uses the implementation in the decorators subpackage. * * @param the type of object the {@link Collection} contains * @param collection the collection to make unmodifiable, must not be null * @return an unmodifiable collection backed by the given collection * @throws IllegalArgumentException if the collection is null */ public static Collection unmodifiableCollection(final Collection collection) { return UnmodifiableCollection.unmodifiableCollection(collection); } /** * Returns a predicated (validating) collection backed by the given collection. *

* Only objects that pass the test in the given predicate can be added to the collection. * Trying to add an invalid object results in an IllegalArgumentException. * It is important not to use the original collection after invoking this method, * as it is a backdoor for adding invalid objects. * * @param collection the collection to predicate, must not be null * @param predicate the predicate for the collection, must not be null * @param the type of objects in the Collection. * @return a predicated collection backed by the given collection * @throws IllegalArgumentException if the Collection is null */ public static Collection predicatedCollection(final Collection collection, final Predicate predicate) { return PredicatedCollection.predicatedCollection(collection, predicate); } /** * Returns a transformed bag backed by the given collection. *

* Each object is passed through the transformer as it is added to the * Collection. It is important not to use the original collection after invoking this * method, as it is a backdoor for adding untransformed objects. *

* Existing entries in the specified collection will not be transformed. * If you want that behaviour, see {@link TransformedCollection#transformedCollection}. * * @param the type of object the {@link Collection} contains * @param collection the collection to predicate, must not be null * @param transformer the transformer for the collection, must not be null * @return a transformed collection backed by the given collection * @throws IllegalArgumentException if the Collection or Transformer is null */ public static Collection transformingCollection(final Collection collection, final Transformer transformer) { return TransformedCollection.transformingCollection(collection, transformer); } /** * Extract the lone element of the specified Collection. * @param collection type * @param collection to read * @return sole member of collection * @throws IllegalArgumentException if collection is null/empty or contains more than one element * @since 4.0 */ public static E extractSingleton(final Collection collection) { if (collection == null || collection.size() != 1) { throw new IllegalArgumentException("Can extract singleton only when collection size == 1"); } return collection.iterator().next(); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 22078; to = 21970.Please, attach your messages.log to new issue! INFO [org.netbeans.modules.java.hints.providers.code.CodeHintProviderImpl$WorkerImpl]: org.netbeans.modules.java.hints.suggestions.TooStrongCast.broadTypeCast java.lang.IllegalArgumentException: OTHER at org.netbeans.api.java.source.ElementHandle.createImpl(ElementHandle.java:484) at org.netbeans.api.java.source.ElementHandle.create(ElementHandle.java:397) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:208) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:201) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:141) at org.netbeans.modules.java.hints.suggestions.TooStrongCast$ReplaceTypeCast.(TooStrongCast.java:296) at org.netbeans.modules.java.hints.suggestions.TooStrongCast.broadTypeCast(TooStrongCast.java:210) Caused: java.lang.reflect.InvocationTargetException at sun.reflect.GeneratedMethodAccessor88.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) [catch] at org.netbeans.modules.java.hints.providers.code.CodeHintProviderImpl$WorkerImpl.createErrors(CodeHintProviderImpl.java:336) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.runHint(HintsInvoker.java:790) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.doComputeHints(HintsInvoker.java:567) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHintsImpl(HintsInvoker.java:294) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:239) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:209) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:199) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:294) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:264) at org.netbeans.api.java.source.JavaSource$MultiTask.run(JavaSource.java:493) at org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask(TaskProcessor.java:593) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:191) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:163) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:206) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:203) at org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager.priorityIO(FileChangedManager.java:176) at org.netbeans.modules.masterfs.providers.ProvidedExtensions.priorityIO(ProvidedExtensions.java:360) at org.netbeans.modules.parsing.impl.Utilities.runPriorityIO(Utilities.java:74) at org.netbeans.modules.parsing.impl.TaskProcessor.runUserTask(TaskProcessor.java:203) at org.netbeans.modules.parsing.api.ParserManager.parse(ParserManager.java:108) at org.netbeans.api.java.source.JavaSource.runUserActionTaskImpl(JavaSource.java:443) at org.netbeans.api.java.source.JavaSource.runUserActionTask(JavaSource.java:414) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getLocalVerifiedSpans(BatchSearch.java:264) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.access$600(BatchSearch.java:99) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$LocalIndexEnquirer.validateResource(BatchSearch.java:574) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getVerifiedSpans(BatchSearch.java:207) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:191) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:134) at org.netbeans.modules.java.hints.spiimpl.refactoring.AbstractApplyHintsRefactoringPlugin.performApplyPattern(AbstractApplyHintsRefactoringPlugin.java:141) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.performApplyPattern(FindDuplicatesRefactoringPlugin.java:121) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.prepare(FindDuplicatesRefactoringPlugin.java:86) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare2(AbstractRefactoring.java:437) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare(AbstractRefactoring.java:421) at org.netbeans.modules.refactoring.api.AbstractRefactoring.prepare(AbstractRefactoring.java:232) at org.netbeans.modules.refactoring.spi.impl.ParametersPanel$Prepare.run(ParametersPanel.java:1026) at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1423) at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2033) SEVERE [org.openide.util.Exceptions] java.lang.IllegalArgumentException: OTHER at org.netbeans.api.java.source.ElementHandle.createImpl(ElementHandle.java:484) at org.netbeans.api.java.source.ElementHandle.create(ElementHandle.java:397) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:208) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:201) at org.netbeans.api.java.source.TypeMirrorHandle.create(TypeMirrorHandle.java:141) at org.netbeans.modules.java.hints.suggestions.TooStrongCast$ReplaceTypeCast.(TooStrongCast.java:296) at org.netbeans.modules.java.hints.suggestions.TooStrongCast.broadTypeCast(TooStrongCast.java:210) at sun.reflect.GeneratedMethodAccessor88.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) [catch] at org.netbeans.modules.java.hints.providers.code.CodeHintProviderImpl$WorkerImpl.createErrors(CodeHintProviderImpl.java:336) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.runHint(HintsInvoker.java:790) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.doComputeHints(HintsInvoker.java:567) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHintsImpl(HintsInvoker.java:294) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:239) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:209) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:199) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:294) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:264) at org.netbeans.api.java.source.JavaSource$MultiTask.run(JavaSource.java:493) at org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask(TaskProcessor.java:593) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:191) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:163) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:206) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:203) at org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager.priorityIO(FileChangedManager.java:176) at org.netbeans.modules.masterfs.providers.ProvidedExtensions.priorityIO(ProvidedExtensions.java:360) at org.netbeans.modules.parsing.impl.Utilities.runPriorityIO(Utilities.java:74) at org.netbeans.modules.parsing.impl.TaskProcessor.runUserTask(TaskProcessor.java:203) at org.netbeans.modules.parsing.api.ParserManager.parse(ParserManager.java:108) at org.netbeans.api.java.source.JavaSource.runUserActionTaskImpl(JavaSource.java:443) at org.netbeans.api.java.source.JavaSource.runUserActionTask(JavaSource.java:414) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getLocalVerifiedSpans(BatchSearch.java:264) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.access$600(BatchSearch.java:99) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$LocalIndexEnquirer.validateResource(BatchSearch.java:574) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getVerifiedSpans(BatchSearch.java:207) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:191) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:134) at org.netbeans.modules.java.hints.spiimpl.refactoring.AbstractApplyHintsRefactoringPlugin.performApplyPattern(AbstractApplyHintsRefactoringPlugin.java:141) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.performApplyPattern(FindDuplicatesRefactoringPlugin.java:121) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.prepare(FindDuplicatesRefactoringPlugin.java:86) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare2(AbstractRefactoring.java:437) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare(AbstractRefactoring.java:421) at org.netbeans.modules.refactoring.api.AbstractRefactoring.prepare(AbstractRefactoring.java:232) at org.netbeans.modules.refactoring.spi.impl.ParametersPanel$Prepare.run(ParametersPanel.java:1026) at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1423) at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2033) ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.bag.HashBag; import org.apache.commons.collections4.collection.PredicatedCollection; import org.apache.commons.collections4.collection.SynchronizedCollection; import org.apache.commons.collections4.collection.TransformedCollection; import org.apache.commons.collections4.collection.UnmodifiableBoundedCollection; import org.apache.commons.collections4.collection.UnmodifiableCollection; import org.apache.commons.collections4.functors.TruePredicate; import org.apache.commons.collections4.iterators.CollatingIterator; import org.apache.commons.collections4.iterators.PermutationIterator; /** * Provides utility methods and decorators for {@link Collection} instances. *

* NOTE: From 4.0, method parameters will take {@link Iterable} objects when possible. * * @since 1.0 * @version $Id: CollectionUtils.java 1540639 2013-11-11 08:54:12Z tn $ */ public class CollectionUtils { /** * Helper class to easily access cardinality properties of two collections. * @param the element type */ private static class CardinalityHelper { /** Contains the cardinality for each object in collection A. */ final Map cardinalityA; /** Contains the cardinality for each object in collection B. */ final Map cardinalityB; /** * Create a new CardinalityHelper for two collections. * @param a the first collection * @param b the second collection */ public CardinalityHelper(final Iterable a, final Iterable b) { cardinalityA = CollectionUtils.getCardinalityMap(a); cardinalityB = CollectionUtils.getCardinalityMap(b); } /** * Returns the maximum frequency of an object. * @param obj the object * @return the maximum frequency of the object */ public final int max(final Object obj) { return Math.max(freqA(obj), freqB(obj)); } /** * Returns the minimum frequency of an object. * @param obj the object * @return the minimum frequency of the object */ public final int min(final Object obj) { return Math.min(freqA(obj), freqB(obj)); } /** * Returns the frequency of this object in collection A. * @param obj the object * @return the frequency of the object in collection A */ public int freqA(final Object obj) { return getFreq(obj, cardinalityA); } /** * Returns the frequency of this object in collection B. * @param obj the object * @return the frequency of the object in collection B */ public int freqB(final Object obj) { return getFreq(obj, cardinalityB); } private final int getFreq(final Object obj, final Map freqMap) { final Integer count = freqMap.get(obj); if (count != null) { return count.intValue(); } return 0; } } /** * Helper class for set-related operations, e.g. union, subtract, intersection. * @param the element type */ private static class SetOperationCardinalityHelper extends CardinalityHelper implements Iterable { /** Contains the unique elements of the two collections. */ private final Set elements; /** Output collection. */ private final List newList; /** * Create a new set operation helper from the two collections. * @param a the first collection * @param b the second collection */ public SetOperationCardinalityHelper(final Iterable a, final Iterable b) { super(a, b); elements = new HashSet(); addAll(elements, a); addAll(elements, b); // the resulting list must contain at least each unique element, but may grow newList = new ArrayList(elements.size()); } public Iterator iterator() { return elements.iterator(); } /** * Add the object {@code count} times to the result collection. * @param obj the object to add * @param count the count */ public void setCardinality(final O obj, final int count) { for (int i = 0; i < count; i++) { newList.add(obj); } } /** * Returns the resulting collection. * @return the result */ public Collection list() { return newList; } } /** * An empty unmodifiable collection. * The JDK provides empty Set and List implementations which could be used for * this purpose. However they could be cast to Set or List which might be * undesirable. This implementation only implements Collection. */ @SuppressWarnings("rawtypes") // we deliberately use the raw type here public static final Collection EMPTY_COLLECTION = UnmodifiableCollection.unmodifiableCollection(new ArrayList()); /** * CollectionUtils should not normally be instantiated. */ private CollectionUtils() {} /** * Returns the immutable EMPTY_COLLECTION with generic type safety. * * @see #EMPTY_COLLECTION * @since 4.0 * @param the element type * @return immutable empty collection */ @SuppressWarnings("unchecked") // OK, empty collection is compatible with any type public static Collection emptyCollection() { return EMPTY_COLLECTION; } /** * Returns an immutable empty collection if the argument is null, * or the argument itself otherwise. * * @param the element type * @param collection the collection, possibly null * @return an empty collection if the argument is null */ @SuppressWarnings("unchecked") // OK, empty collection is compatible with any type public static Collection emptyIfNull(final Collection collection) { return collection == null ? EMPTY_COLLECTION : collection; } /** * Returns a {@link Collection} containing the union of the given * {@link Iterable}s. *

* The cardinality of each element in the returned {@link Collection} will * be equal to the maximum of the cardinality of that element in the two * given {@link Iterable}s. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the union of the two collections * @see Collection#addAll */ public static Collection union(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.max(obj)); } return helper.list(); } /** * Returns a {@link Collection} containing the intersection of the given * {@link Iterable}s. *

* The cardinality of each element in the returned {@link Collection} will * be equal to the minimum of the cardinality of that element in the two * given {@link Iterable}s. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the intersection of the two collections * @see Collection#retainAll * @see #containsAny */ public static Collection intersection(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.min(obj)); } return helper.list(); } /** * Returns a {@link Collection} containing the exclusive disjunction * (symmetric difference) of the given {@link Iterable}s. *

* The cardinality of each element e in the returned * {@link Collection} will be equal to * max(cardinality(e,a),cardinality(e,b)) - min(cardinality(e,a), * cardinality(e,b)). *

* This is equivalent to * {@link #subtract subtract}({@link #union union(a,b)},{@link #intersection intersection(a,b)}) * or * {@link #union union}({@link #subtract subtract(a,b)},{@link #subtract subtract(b,a)}). * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the symmetric difference of the two collections */ public static Collection disjunction(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.max(obj) - helper.min(obj)); } return helper.list(); } /** * Returns a new {@link Collection} containing a - b. * The cardinality of each element e in the returned {@link Collection} * will be the cardinality of e in a minus the cardinality * of e in b, or zero, whichever is greater. * * @param a the collection to subtract from, must not be null * @param b the collection to subtract, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return a new collection with the results * @see Collection#removeAll */ public static Collection subtract(final Iterable a, final Iterable b) { final Predicate p = TruePredicate.truePredicate(); return subtract(a, b, p); } /** * Returns a new {@link Collection} containing a minus a subset of * b. Only the elements of b that satisfy the predicate * condition, p are subtracted from a. * *

The cardinality of each element e in the returned {@link Collection} * that satisfies the predicate condition will be the cardinality of e in a * minus the cardinality of e in b, or zero, whichever is greater.

*

The cardinality of each element e in the returned {@link Collection} that does not * satisfy the predicate condition will be equal to the cardinality of e in a.

* * @param a the collection to subtract from, must not be null * @param b the collection to subtract, must not be null * @param p the condition used to determine which elements of b are * subtracted. * @param the generic type that is able to represent the types contained * in both input collections. * @return a new collection with the results * @since 4.0 * @see Collection#removeAll */ public static Collection subtract(final Iterable a, final Iterable b, final Predicate p) { final ArrayList list = new ArrayList(); final HashBag bag = new HashBag(); for (final O element : b) { if (p.evaluate(element)) { bag.add(element); } } for (final O element : a) { if (!bag.remove(element, 1)) { list.add(element); } } return list; } /** * Returns true iff all elements of {@code coll2} are also contained * in {@code coll1}. The cardinality of values in {@code coll2} is not taken into account, * which is the same behavior as {@link Collection#containsAll(Collection)}. *

* In other words, this method returns true iff the * {@link #intersection} of coll1 and coll2 has the same cardinality as * the set of unique values from {@code coll2}. In case {@code coll2} is empty, {@code true} * will be returned. *

* This method is intended as a replacement for {@link Collection#containsAll(Collection)} * with a guaranteed runtime complexity of {@code O(n + m)}. Depending on the type of * {@link Collection} provided, this method will be much faster than calling * {@link Collection#containsAll(Collection)} instead, though this will come at the * cost of an additional space complexity O(n). * * @param coll1 the first collection, must not be null * @param coll2 the second collection, must not be null * @return true iff the intersection of the collections has the same cardinality * as the set of unique elements from the second collection * @since 4.0 */ public static boolean containsAll(final Collection coll1, final Collection coll2) { if (coll2.isEmpty()) { return true; } else { final Iterator it = coll1.iterator(); final Set elementsAlreadySeen = new HashSet(); for (final Object nextElement : coll2) { if (elementsAlreadySeen.contains(nextElement)) { continue; } boolean foundCurrentElement = false; while (it.hasNext()) { final Object p = it.next(); elementsAlreadySeen.add(p); if (nextElement == null ? p == null : nextElement.equals(p)) { foundCurrentElement = true; break; } } if (foundCurrentElement) { continue; } else { return false; } } return true; } } /** * Returns true iff at least one element is in both collections. *

* In other words, this method returns true iff the * {@link #intersection} of coll1 and coll2 is not empty. * * @param coll1 the first collection, must not be null * @param coll2 the second collection, must not be null * @return true iff the intersection of the collections is non-empty * @since 2.1 * @see #intersection */ public static boolean containsAny(final Collection coll1, final Collection coll2) { if (coll1.size() < coll2.size()) { for (final Object aColl1 : coll1) { if (coll2.contains(aColl1)) { return true; } } } else { for (final Object aColl2 : coll2) { if (coll1.contains(aColl2)) { return true; } } } return false; } /** * Returns a {@link Map} mapping each unique element in the given * {@link Collection} to an {@link Integer} representing the number * of occurrences of that element in the {@link Collection}. *

* Only those elements present in the collection will appear as * keys in the map. * * @param the type of object in the returned {@link Map}. This is a super type of . * @param coll the collection to get the cardinality map for, must not be null * @return the populated cardinality map */ public static Map getCardinalityMap(final Iterable coll) { final Map count = new HashMap(); for (final O obj : coll) { final Integer c = count.get(obj); if (c == null) { count.put(obj, Integer.valueOf(1)); } else { count.put(obj, Integer.valueOf(c.intValue() + 1)); } } return count; } /** * Returns true iff a is a sub-collection of b, * that is, iff the cardinality of e in a is less than or * equal to the cardinality of e in b, for each element e * in a. * * @param a the first (sub?) collection, must not be null * @param b the second (super?) collection, must not be null * @return true iff a is a sub-collection of b * @see #isProperSubCollection * @see Collection#containsAll */ public static boolean isSubCollection(final Collection a, final Collection b) { final CardinalityHelper helper = new CardinalityHelper(a, b); for (final Object obj : a) { if (helper.freqA(obj) > helper.freqB(obj)) { return false; } } return true; } /** * Returns true iff a is a proper sub-collection of b, * that is, iff the cardinality of e in a is less * than or equal to the cardinality of e in b, * for each element e in a, and there is at least one * element f such that the cardinality of f in b * is strictly greater than the cardinality of f in a. *

* The implementation assumes *

    *
  • a.size() and b.size() represent the * total cardinality of a and b, resp.
  • *
  • a.size() < Integer.MAXVALUE
  • *
* * @param a the first (sub?) collection, must not be null * @param b the second (super?) collection, must not be null * @return true iff a is a proper sub-collection of b * @see #isSubCollection * @see Collection#containsAll */ public static boolean isProperSubCollection(final Collection a, final Collection b) { return a.size() < b.size() && CollectionUtils.isSubCollection(a, b); } /** * Returns true iff the given {@link Collection}s contain * exactly the same elements with exactly the same cardinalities. *

* That is, iff the cardinality of e in a is * equal to the cardinality of e in b, * for each element e in a or b. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @return true iff the collections contain the same elements with the same cardinalities. */ public static boolean isEqualCollection(final Collection a, final Collection b) { if(a.size() != b.size()) { return false; } final CardinalityHelper helper = new CardinalityHelper(a, b); if(helper.cardinalityA.size() != helper.cardinalityB.size()) { return false; } for( final Object obj : helper.cardinalityA.keySet()) { if(helper.freqA(obj) != helper.freqB(obj)) { return false; } } return true; } /** * Returns true iff the given {@link Collection}s contain * exactly the same elements with exactly the same cardinalities. *

* That is, iff the cardinality of e in a is * equal to the cardinality of e in b, * for each element e in a or b. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param equator the Equator used for testing equality * @return true iff the collections contain the same elements with the same cardinalities. * @throws IllegalArgumentException if the equator is null * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) // we don't know the types due to wildcards in the signature public static boolean isEqualCollection(final Collection a, final Collection b, final Equator equator) { if (equator == null) { throw new IllegalArgumentException("equator may not be null"); } if(a.size() != b.size()) { return false; } final Transformer transformer = new Transformer() { public EquatorWrapper transform(final Object input) { return new EquatorWrapper(equator, input); } }; return isEqualCollection(collect(a, transformer), collect(b, transformer)); } /** * Wraps another object and uses the provided Equator to implement * {@link #equals(Object)} and {@link #hashCode()}. *

* This class can be used to store objects into a Map. * * @param the element type * @since 4.0 */ private static class EquatorWrapper { private final Equator equator; private final O object; public EquatorWrapper(final Equator equator, final O object) { this.equator = equator; this.object = object; } public O getObject() { return object; } @Override public boolean equals(final Object obj) { if (!(obj instanceof EquatorWrapper)) { return false; } @SuppressWarnings("unchecked") final EquatorWrapper otherObj = (EquatorWrapper) obj; return equator.equate(object, otherObj.getObject()); } @Override public int hashCode() { return equator.hash(object); } } /** * Returns the number of occurrences of obj in coll. * * @param obj the object to find the cardinality of * @param coll the {@link Iterable} to search * @param the type of object that the {@link Iterable} may contain. * @return the the number of occurrences of obj in coll */ public static int cardinality(final O obj, final Iterable coll) { if (coll instanceof Set) { return ((Set) coll).contains(obj) ? 1 : 0; } if (coll instanceof Bag) { return ((Bag) coll).getCount(obj); } int count = 0; if (obj == null) { for (final Object element : coll) { if (element == null) { count++; } } } else { for (final Object element : coll) { if (obj.equals(element)) { count++; } } } return count; } /** * Finds the first element in the given collection which matches the given predicate. *

* If the input collection or predicate is null, or no element of the collection * matches the predicate, null is returned. * * @param the type of object the {@link Iterable} contains * @param collection the collection to search, may be null * @param predicate the predicate to use, may be null * @return the first element of the collection which matches the predicate or null if none could be found */ public static T find(final Iterable collection, final Predicate predicate) { if (collection != null && predicate != null) { for (final T item : collection) { if (predicate.evaluate(item)) { return item; } } } return null; } /** * Executes the given closure on each element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param the closure type * @param collection the collection to get the input from, may be null * @param closure the closure to perform, may be null * @return closure */ public static > C forAllDo(final Iterable collection, final C closure) { if (collection != null && closure != null) { for (final T element : collection) { closure.execute(element); } } return closure; } /** * Executes the given closure on each element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterator} contains * @param the closure type * @param iterator the iterator to get the input from, may be null * @param closure the closure to perform, may be null * @return closure * @since 4.0 */ public static > C forAllDo(final Iterator iterator, final C closure) { if (iterator != null && closure != null) { while (iterator.hasNext()) { closure.execute(iterator.next()); } } return closure; } /** * Executes the given closure on each but the last element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param the closure type * @param collection the collection to get the input from, may be null * @param closure the closure to perform, may be null * @return the last element in the collection, or null if either collection or closure is null * @since 4.0 */ public static > T forAllButLastDo(final Iterable collection, final C closure) { return collection != null && closure != null ? forAllButLastDo(collection.iterator(), closure) : null; } /** * Executes the given closure on each but the last element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Collection} contains * @param the closure type * @param iterator the iterator to get the input from, may be null * @param closure the closure to perform, may be null * @return the last element in the collection, or null if either iterator or closure is null * @since 4.0 */ public static > T forAllButLastDo(final Iterator iterator, final C closure) { if (iterator != null && closure != null) { while (iterator.hasNext()) { final T element = iterator.next(); if (iterator.hasNext()) { closure.execute(element); } else { return element; } } } return null; } /** * Filter the collection by applying a Predicate to each element. If the * predicate returns false, remove the element. *

* If the input collection or predicate is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param collection the collection to get the input from, may be null * @param predicate the predicate to use as a filter, may be null * @return true if the collection is modified by this call, false otherwise. */ public static boolean filter(final Iterable collection, final Predicate predicate) { boolean result = false; if (collection != null && predicate != null) { for (final Iterator it = collection.iterator(); it.hasNext();) { if (!predicate.evaluate(it.next())) { it.remove(); result = true; } } } return result; } /** * Filter the collection by applying a Predicate to each element. If the * predicate returns true, remove the element. *

* This is equivalent to

filter(collection, PredicateUtils.notPredicate(predicate))
* if predicate is != null. *

* If the input collection or predicate is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param collection the collection to get the input from, may be null * @param predicate the predicate to use as a filter, may be null * @return true if the collection is modified by this call, false otherwise. */ public static boolean filterInverse(final Iterable collection, final Predicate predicate) { return filter(collection, predicate == null ? null : PredicateUtils.notPredicate(predicate)); } /** * Transform the collection by applying a Transformer to each element. *

* If the input collection or transformer is null, there is no change made. *

* This routine is best for Lists, for which set() is used to do the * transformations "in place." For other Collections, clear() and addAll() * are used to replace elements. *

* If the input collection controls its input, such as a Set, and the * Transformer creates duplicates (or are otherwise invalid), the collection * may reduce in size due to calling this method. * * @param the type of object the {@link Collection} contains * @param collection the {@link Collection} to get the input from, may be null * @param transformer the transformer to perform, may be null */ public static void transform(final Collection collection, final Transformer transformer) { if (collection != null && transformer != null) { if (collection instanceof List) { final List list = (List) collection; for (final ListIterator it = list.listIterator(); it.hasNext();) { it.set(transformer.transform(it.next())); } } else { final Collection resultCollection = collect(collection, transformer); collection.clear(); collection.addAll(resultCollection); } } } /** * Counts the number of elements in the input collection that match the * predicate. *

* A null collection or predicate matches no elements. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return the number of matches for the predicate in the collection */ public static int countMatches(final Iterable input, final Predicate predicate) { int count = 0; if (input != null && predicate != null) { for (final C o : input) { if (predicate.evaluate(o)) { count++; } } } return count; } /** * Answers true if a predicate is true for at least one element of a * collection. *

* A null collection or predicate returns false. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return true if at least one element of the collection matches the predicate */ public static boolean exists(final Iterable input, final Predicate predicate) { if (input != null && predicate != null) { for (final C o : input) { if (predicate.evaluate(o)) { return true; } } } return false; } /** * Answers true if a predicate is true for every element of a * collection. *

* A null predicate returns false.
* A null or empty collection returns true. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return true if every element of the collection matches the predicate or if the * collection is empty, false otherwise * @since 4.0 */ public static boolean matchesAll(final Iterable input, final Predicate predicate) { if (predicate == null) { return false; } if (input != null) { for (final C o : input) { if (!predicate.evaluate(o)) { return false; } } } return true; } /** * Selects all elements from input collection which match the given * predicate into an output collection. *

* A null predicate matches no elements. * * @param the type of object the {@link Iterable} contains * @param inputCollection the collection to get the input from, may not be null * @param predicate the predicate to use, may be null * @return the elements matching the predicate (new list) * @throws NullPointerException if the input collection is null */ public static Collection select(final Iterable inputCollection, final Predicate predicate) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return select(inputCollection, predicate, answer); } /** * Selects all elements from input collection which match the given * predicate and adds them to outputCollection. *

* If the input collection or predicate is null, there is no change to the * output collection. * * @param the type of object the {@link Iterable} contains * @param the type of the output {@link Collection} * @param inputCollection the collection to get the input from, may be null * @param predicate the predicate to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and predicate or not null * @return the outputCollection */ public static > R select(final Iterable inputCollection, final Predicate predicate, final R outputCollection) { if (inputCollection != null && predicate != null) { for (final O item : inputCollection) { if (predicate.evaluate(item)) { outputCollection.add(item); } } } return outputCollection; } /** * Selects all elements from inputCollection which don't match the given * predicate into an output collection. *

* If the input predicate is null, the result is an empty * list. * * @param the type of object the {@link Iterable} contains * @param inputCollection the collection to get the input from, may not be null * @param predicate the predicate to use, may be null * @return the elements not matching the predicate (new list) * @throws NullPointerException if the input collection is null */ public static Collection selectRejected(final Iterable inputCollection, final Predicate predicate) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return selectRejected(inputCollection, predicate, answer); } /** * Selects all elements from inputCollection which don't match the given * predicate and adds them to outputCollection. *

* If the input predicate is null, no elements are added to * outputCollection. * * @param the type of object the {@link Iterable} contains * @param the type of the output {@link Collection} * @param inputCollection the collection to get the input from, may be null * @param predicate the predicate to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and predicate or not null * @return outputCollection */ public static > R selectRejected(final Iterable inputCollection, final Predicate predicate, final R outputCollection) { if (inputCollection != null && predicate != null) { for (final O item : inputCollection) { if (!predicate.evaluate(item)) { outputCollection.add(item); } } } return outputCollection; } /** * Returns a new Collection consisting of the elements of inputCollection * transformed by the given transformer. *

* If the input transformer is null, the result is an empty list. * * @param the type of object in the input collection * @param the type of object in the output collection * @param inputCollection the collection to get the input from, may not be null * @param transformer the transformer to use, may be null * @return the transformed result (new list) * @throws NullPointerException if the input collection is null */ public static Collection collect(final Iterable inputCollection, final Transformer transformer) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return collect(inputCollection, transformer, answer); } /** * Transforms all elements from the inputIterator with the given transformer * and adds them to the outputCollection. *

* If the input iterator or transformer is null, the result is an empty * list. * * @param inputIterator the iterator to get the input from, may be null * @param transformer the transformer to use, may be null * @param the type of object in the input collection * @param the type of object in the output collection * @return the transformed result (new list) */ public static Collection collect(final Iterator inputIterator, final Transformer transformer) { return collect(inputIterator, transformer, new ArrayList()); } /** * Transforms all elements from inputCollection with the given transformer * and adds them to the outputCollection. *

* If the input collection or transformer is null, there is no change to the * output collection. * * @param the type of object in the input collection * @param the type of object in the output collection * @param the output type of the transformer - this extends O. * @param inputCollection the collection to get the input from, may be null * @param transformer the transformer to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and transformer are not null * @return the outputCollection with the transformed input added * @throws NullPointerException if the output collection is null and both, inputCollection and * transformer are not null */ public static > R collect(final Iterable inputCollection, final Transformer transformer, final R outputCollection) { if (inputCollection != null) { return collect(inputCollection.iterator(), transformer, outputCollection); } return outputCollection; } /** * Transforms all elements from the inputIterator with the given transformer * and adds them to the outputCollection. *

* If the input iterator or transformer is null, there is no change to the * output collection. * * @param inputIterator the iterator to get the input from, may be null * @param transformer the transformer to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and transformer are not null * @param the type of object in the input collection * @param the type of object in the output collection * @param the output type of the transformer - this extends O. * @return the outputCollection with the transformed input added * @throws NullPointerException if the output collection is null and both, inputCollection and * transformer are not null */ public static > R collect(final Iterator inputIterator, final Transformer transformer, final R outputCollection) { if (inputIterator != null && transformer != null) { while (inputIterator.hasNext()) { final I item = inputIterator.next(); final O value = transformer.transform(item); outputCollection.add(value); } } return outputCollection; } //----------------------------------------------------------------------- /** * Adds an element to the collection unless the element is null. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param object the object to add, if null it will not be added * @return true if the collection changed * @throws NullPointerException if the collection is null * @since 3.2 */ public static boolean addIgnoreNull(final Collection collection, final T object) { if (collection == null) { throw new NullPointerException("The collection must not be null"); } return object != null && collection.add(object); } /** * Adds all elements in the {@link Iterable} to the given collection. If the * {@link Iterable} is a {@link Collection} then it is cast and will be * added using {@link Collection#addAll(Collection)} instead of iterating. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param iterable the iterable of elements to add, must not be null * @return a boolean indicating whether the collection has changed or not. * @throws NullPointerException if the collection or iterator is null */ public static boolean addAll(final Collection collection, final Iterable iterable) { if (iterable instanceof Collection) { return collection.addAll((Collection) iterable); } return addAll(collection, iterable.iterator()); } /** * Adds all elements in the iteration to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param iterator the iterator of elements to add, must not be null * @return a boolean indicating whether the collection has changed or not. * @throws NullPointerException if the collection or iterator is null */ public static boolean addAll(final Collection collection, final Iterator iterator) { boolean changed = false; while (iterator.hasNext()) { changed |= collection.add(iterator.next()); } return changed; } /** * Adds all elements in the enumeration to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param enumeration the enumeration of elements to add, must not be null * @return {@code true} if the collections was changed, {@code false} otherwise * @throws NullPointerException if the collection or enumeration is null */ public static boolean addAll(final Collection collection, final Enumeration enumeration) { boolean changed = false; while (enumeration.hasMoreElements()) { changed |= collection.add(enumeration.nextElement()); } return changed; } /** * Adds all elements in the array to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param elements the array of elements to add, must not be null * @return {@code true} if the collection was changed, {@code false} otherwise * @throws NullPointerException if the collection or array is null */ public static boolean addAll(final Collection collection, final C[] elements) { boolean changed = false; for (final C element : elements) { changed |= collection.add(element); } return changed; } /** * Returns the index-th value in {@link Iterator}, throwing * IndexOutOfBoundsException if there is no such element. *

* The Iterator is advanced to index (or to the end, if * index exceeds the number of entries) as a side effect of this method. * * @param iterator the iterator to get a value from * @param index the index to get * @param the type of object in the {@link Iterator} * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid * @throws IllegalArgumentException if the object type is invalid */ public static T get(final Iterator iterator, final int index) { int i = index; checkIndexBounds(i); while (iterator.hasNext()) { i--; if (i == -1) { return iterator.next(); } iterator.next(); } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } /** * Ensures an index is not negative. * @param index the index to check. * @throws IndexOutOfBoundsException if the index is negative. */ private static void checkIndexBounds(final int index) { if (index < 0) { throw new IndexOutOfBoundsException("Index cannot be negative: " + index); } } /** * Returns the index-th value in the iterable's {@link Iterator}, throwing * IndexOutOfBoundsException if there is no such element. *

* If the {@link Iterable} is a {@link List}, then it will use {@link List#get(int)}. * * @param iterable the {@link Iterable} to get a value from * @param index the index to get * @param the type of object in the {@link Iterable}. * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid */ public static T get(final Iterable iterable, final int index) { checkIndexBounds(index); if (iterable instanceof List) { return ((List) iterable).get(index); } return get(iterable.iterator(), index); } /** * Returns the index-th value in object, throwing * IndexOutOfBoundsException if there is no such element or * IllegalArgumentException if object is not an * instance of one of the supported types. *

* The supported types, and associated semantics are: *

    *
  • Map -- the value returned is the Map.Entry in position * index in the map's entrySet iterator, * if there is such an entry.
  • *
  • List -- this method is equivalent to the list's get method.
  • *
  • Array -- the index-th array entry is returned, * if there is such an entry; otherwise an IndexOutOfBoundsException * is thrown.
  • *
  • Collection -- the value returned is the index-th object * returned by the collection's default iterator, if there is such an element.
  • *
  • Iterator or Enumeration -- the value returned is the * index-th object in the Iterator/Enumeration, if there * is such an element. The Iterator/Enumeration is advanced to * index (or to the end, if index exceeds the * number of entries) as a side effect of this method.
  • *
* * @param object the object to get a value from * @param index the index to get * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid * @throws IllegalArgumentException if the object type is invalid */ public static Object get(final Object object, final int index) { int i = index; if (i < 0) { throw new IndexOutOfBoundsException("Index cannot be negative: " + i); } if (object instanceof Map) { final Map map = (Map) object; final Iterator iterator = map.entrySet().iterator(); return get(iterator, i); } else if (object instanceof Object[]) { return ((Object[]) object)[i]; } else if (object instanceof Iterator) { final Iterator it = (Iterator) object; while (it.hasNext()) { i--; if (i == -1) { return it.next(); } it.next(); } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } else if (object instanceof Collection) { final Iterator iterator = ((Collection) object).iterator(); return get(iterator, i); } else if (object instanceof Enumeration) { final Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { i--; if (i == -1) { return it.nextElement(); } else { it.nextElement(); } } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } else if (object == null) { throw new IllegalArgumentException("Unsupported object type: null"); } else { try { return Array.get(object, i); } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } /** * Returns the index-th Map.Entry in the map's entrySet, * throwing IndexOutOfBoundsException if there is no such element. * * @param the key type in the {@link Map} * @param the key type in the {@link Map} * @param map the object to get a value from * @param index the index to get * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid */ public static Map.Entry get(final Map map, final int index) { checkIndexBounds(index); return get(map.entrySet(), index); } /** * Gets the size of the collection/iterator specified. *

* This method can handles objects as follows *

    *
  • Collection - the collection size *
  • Map - the map size *
  • Array - the array size *
  • Iterator - the number of elements remaining in the iterator *
  • Enumeration - the number of elements remaining in the enumeration *
* * @param object the object to get the size of, may be null * @return the size of the specified collection or 0 if the object was null * @throws IllegalArgumentException thrown if object is not recognised * @since 3.1 */ public static int size(final Object object) { if (object == null) { return 0; } int total = 0; if (object instanceof Map) { total = ((Map) object).size(); } else if (object instanceof Collection) { total = ((Collection) object).size(); } else if (object instanceof Object[]) { total = ((Object[]) object).length; } else if (object instanceof Iterator) { final Iterator it = (Iterator) object; while (it.hasNext()) { total++; it.next(); } } else if (object instanceof Enumeration) { final Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { total++; it.nextElement(); } } else { try { total = Array.getLength(object); } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } return total; } /** * Checks if the specified collection/array/iterator is empty. *

* This method can handles objects as follows *

    *
  • Collection - via collection isEmpty *
  • Map - via map isEmpty *
  • Array - using array size *
  • Iterator - via hasNext *
  • Enumeration - via hasMoreElements *
*

* Note: This method is named to avoid clashing with * {@link #isEmpty(Collection)}. * * @param object the object to get the size of, may be null * @return true if empty or null * @throws IllegalArgumentException thrown if object is not recognised * @since 3.2 */ public static boolean sizeIsEmpty(final Object object) { if (object == null) { return true; } else if (object instanceof Collection) { return ((Collection) object).isEmpty(); } else if (object instanceof Map) { return ((Map) object).isEmpty(); } else if (object instanceof Object[]) { return ((Object[]) object).length == 0; } else if (object instanceof Iterator) { return ((Iterator) object).hasNext() == false; } else if (object instanceof Enumeration) { return ((Enumeration) object).hasMoreElements() == false; } else { try { return Array.getLength(object) == 0; } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } //----------------------------------------------------------------------- /** * Null-safe check if the specified collection is empty. *

* Null returns true. * * @param coll the collection to check, may be null * @return true if empty or null * @since 3.2 */ public static boolean isEmpty(final Collection coll) { return coll == null || coll.isEmpty(); } /** * Null-safe check if the specified collection is not empty. *

* Null returns false. * * @param coll the collection to check, may be null * @return true if non-null and non-empty * @since 3.2 */ public static boolean isNotEmpty(final Collection coll) { return !isEmpty(coll); } //----------------------------------------------------------------------- /** * Reverses the order of the given array. * * @param array the array to reverse */ public static void reverseArray(final Object[] array) { int i = 0; int j = array.length - 1; Object tmp; while (j > i) { tmp = array[j]; array[j] = array[i]; array[i] = tmp; j--; i++; } } /** * Returns true if no more elements can be added to the Collection. *

* This method uses the {@link BoundedCollection} interface to determine the * full status. If the collection does not implement this interface then * false is returned. *

* The collection does not have to implement this interface directly. * If the collection has been decorated using the decorators subpackage * then these will be removed to access the BoundedCollection. * * @param coll the collection to check * @return true if the BoundedCollection is full * @throws NullPointerException if the collection is null */ public static boolean isFull(final Collection coll) { if (coll == null) { throw new NullPointerException("The collection must not be null"); } if (coll instanceof BoundedCollection) { return ((BoundedCollection) coll).isFull(); } try { final BoundedCollection bcoll = UnmodifiableBoundedCollection.unmodifiableBoundedCollection(coll); return bcoll.isFull(); } catch (final IllegalArgumentException ex) { return false; } } /** * Get the maximum number of elements that the Collection can contain. *

* This method uses the {@link BoundedCollection} interface to determine the * maximum size. If the collection does not implement this interface then * -1 is returned. *

* The collection does not have to implement this interface directly. * If the collection has been decorated using the decorators subpackage * then these will be removed to access the BoundedCollection. * * @param coll the collection to check * @return the maximum size of the BoundedCollection, -1 if no maximum size * @throws NullPointerException if the collection is null */ public static int maxSize(final Collection coll) { if (coll == null) { throw new NullPointerException("The collection must not be null"); } if (coll instanceof BoundedCollection) { return ((BoundedCollection) coll).maxSize(); } try { final BoundedCollection bcoll = UnmodifiableBoundedCollection.unmodifiableBoundedCollection(coll); return bcoll.maxSize(); } catch (final IllegalArgumentException ex) { return -1; } } //----------------------------------------------------------------------- /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the natural ordering of the elements is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection is null * @since 4.0 */ public static > List collate(Iterable a, Iterable b) { return collate(a, b, ComparatorUtils.naturalComparator(), true); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the natural ordering of the elements is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param includeDuplicates if {@code true} duplicate elements will be retained, otherwise * they will be removed in the output collection * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection is null * @since 4.0 */ public static > List collate(final Iterable a, final Iterable b, final boolean includeDuplicates) { return collate(a, b, ComparatorUtils.naturalComparator(), includeDuplicates); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the ordering of the elements according to Comparator c is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param c the comparator to use for the merge. * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection or the comparator is null * @since 4.0 */ public static List collate(final Iterable a, final Iterable b, final Comparator c) { return collate(a, b, c, true); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the ordering of the elements according to Comparator c is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param c the comparator to use for the merge. * @param includeDuplicates if {@code true} duplicate elements will be retained, otherwise * they will be removed in the output collection * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection or the comparator is null * @since 4.0 */ public static List collate(final Iterable a, final Iterable b, final Comparator c, final boolean includeDuplicates) { if (a == null || b == null) { throw new IllegalArgumentException("The collections must not be null"); } if (c == null) { throw new IllegalArgumentException("The comparator must not be null"); } // if both Iterables are a Collection, we can estimate the size final int totalSize = a instanceof Collection && b instanceof Collection ? Math.max(1, ((Collection) a).size() + ((Collection) b).size()) : 10; final Iterator iterator = new CollatingIterator(c, a.iterator(), b.iterator()); if (includeDuplicates) { return IteratorUtils.toList(iterator, totalSize); } else { final ArrayList mergedList = new ArrayList(totalSize); O lastItem = null; while (iterator.hasNext()) { final O item = iterator.next(); if (lastItem == null || !lastItem.equals(item)) { mergedList.add(item); } lastItem = item; } mergedList.trimToSize(); return mergedList; } } //----------------------------------------------------------------------- /** * Returns a {@link Collection} of all the permutations of the input collection. *

* NOTE: the number of permutations of a given collection is equal to n!, where * n is the size of the collection. Thus, the resulting collection will become * very large for collections > 10 (e.g. 10! = 3628800, 15! = 1307674368000). *

* For larger collections it is advised to use a {@link PermutationIterator} to * iterate over all permutations. * * @see PermutationIterator * * @param the element type * @param collection the collection to create permutations for, may not be null * @return an unordered collection of all permutations of the input collection * @throws NullPointerException if collection is null * @since 4.0 */ public static Collection> permutations(final Collection collection) { final PermutationIterator it = new PermutationIterator(collection); final Collection> result = new LinkedList>(); while (it.hasNext()) { result.add(it.next()); } return result; } //----------------------------------------------------------------------- /** * Returns a collection containing all the elements in collection * that are also in retain. The cardinality of an element e * in the returned collection is the same as the cardinality of e * in collection unless retain does not contain e, in which * case the cardinality is zero. This method is useful if you do not wish to modify * the collection c and thus cannot call c.retainAll(retain);. * * @param the type of object the {@link Collection} contains * @param collection the collection whose contents are the target of the #retailAll operation * @param retain the collection containing the elements to be retained in the returned collection * @return a Collection containing all the elements of collection * that occur at least once in retain. * @throws NullPointerException if either parameter is null * @since 3.2 */ public static Collection retainAll(final Collection collection, final Collection retain) { return ListUtils.retainAll(collection, retain); } /** * Removes the elements in remove from collection. That is, this * method returns a collection containing all the elements in c * that are not in remove. The cardinality of an element e * in the returned collection is the same as the cardinality of e * in collection unless remove contains e, in which * case the cardinality is zero. This method is useful if you do not wish to modify * the collection c and thus cannot call collection.removeAll(remove);. * * @param the type of object the {@link Collection} contains * @param collection the collection from which items are removed (in the returned collection) * @param remove the items to be removed from the returned collection * @return a Collection containing all the elements of collection except * any elements that also occur in remove. * @throws NullPointerException if either parameter is null * @since 4.0 (method existed in 3.2 but was completely broken) */ public static Collection removeAll(final Collection collection, final Collection remove) { return ListUtils.removeAll(collection, remove); } //----------------------------------------------------------------------- /** * Returns a synchronized collection backed by the given collection. *

* You must manually synchronize on the returned buffer's iterator to * avoid non-deterministic behavior: * *

     * Collection c = CollectionUtils.synchronizedCollection(myCollection);
     * synchronized (c) {
     *     Iterator i = c.iterator();
     *     while (i.hasNext()) {
     *         process (i.next());
     *     }
     * }
     * 
* * This method uses the implementation in the decorators subpackage. * * @param the type of object the {@link Collection} contains * @param collection the collection to synchronize, must not be null * @return a synchronized collection backed by the given collection * @throws IllegalArgumentException if the collection is null */ public static Collection synchronizedCollection(final Collection collection) { return SynchronizedCollection.synchronizedCollection(collection); } /** * Returns an unmodifiable collection backed by the given collection. *

* This method uses the implementation in the decorators subpackage. * * @param the type of object the {@link Collection} contains * @param collection the collection to make unmodifiable, must not be null * @return an unmodifiable collection backed by the given collection * @throws IllegalArgumentException if the collection is null */ public static Collection unmodifiableCollection(final Collection collection) { return UnmodifiableCollection.unmodifiableCollection(collection); } /** * Returns a predicated (validating) collection backed by the given collection. *

* Only objects that pass the test in the given predicate can be added to the collection. * Trying to add an invalid object results in an IllegalArgumentException. * It is important not to use the original collection after invoking this method, * as it is a backdoor for adding invalid objects. * * @param collection the collection to predicate, must not be null * @param predicate the predicate for the collection, must not be null * @param the type of objects in the Collection. * @return a predicated collection backed by the given collection * @throws IllegalArgumentException if the Collection is null */ public static Collection predicatedCollection(final Collection collection, final Predicate predicate) { return PredicatedCollection.predicatedCollection(collection, predicate); } /** * Returns a transformed bag backed by the given collection. *

* Each object is passed through the transformer as it is added to the * Collection. It is important not to use the original collection after invoking this * method, as it is a backdoor for adding untransformed objects. *

* Existing entries in the specified collection will not be transformed. * If you want that behaviour, see {@link TransformedCollection#transformedCollection}. * * @param the type of object the {@link Collection} contains * @param collection the collection to predicate, must not be null * @param transformer the transformer for the collection, must not be null * @return a transformed collection backed by the given collection * @throws IllegalArgumentException if the Collection or Transformer is null */ public static Collection transformingCollection(final Collection collection, final Transformer transformer) { return TransformedCollection.transformingCollection(collection, transformer); } /** * Extract the lone element of the specified Collection. * @param collection type * @param collection to read * @return sole member of collection * @throws IllegalArgumentException if collection is null/empty or contains more than one element * @since 4.0 */ public static E extractSingleton(final Collection collection) { if (collection == null || collection.size() != 1) { throw new IllegalArgumentException("Can extract singleton only when collection size == 1"); } return collection.iterator().next(); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 7424; to = 7341.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.bag.HashBag; import org.apache.commons.collections4.collection.PredicatedCollection; import org.apache.commons.collections4.collection.SynchronizedCollection; import org.apache.commons.collections4.collection.TransformedCollection; import org.apache.commons.collections4.collection.UnmodifiableBoundedCollection; import org.apache.commons.collections4.collection.UnmodifiableCollection; import org.apache.commons.collections4.functors.TruePredicate; import org.apache.commons.collections4.iterators.CollatingIterator; import org.apache.commons.collections4.iterators.PermutationIterator; /** * Provides utility methods and decorators for {@link Collection} instances. *

* NOTE: From 4.0, method parameters will take {@link Iterable} objects when possible. * * @since 1.0 * @version $Id: CollectionUtils.java 1540639 2013-11-11 08:54:12Z tn $ */ public class CollectionUtils { /** * Helper class to easily access cardinality properties of two collections. * @param the element type */ private static class CardinalityHelper { /** Contains the cardinality for each object in collection A. */ final Map cardinalityA; /** Contains the cardinality for each object in collection B. */ final Map cardinalityB; /** * Create a new CardinalityHelper for two collections. * @param a the first collection * @param b the second collection */ public CardinalityHelper(final Iterable a, final Iterable b) { cardinalityA = CollectionUtils.getCardinalityMap(a); cardinalityB = CollectionUtils.getCardinalityMap(b); } /** * Returns the maximum frequency of an object. * @param obj the object * @return the maximum frequency of the object */ public final int max(final Object obj) { return Math.max(freqA(obj), freqB(obj)); } /** * Returns the minimum frequency of an object. * @param obj the object * @return the minimum frequency of the object */ public final int min(final Object obj) { return Math.min(freqA(obj), freqB(obj)); } /** * Returns the frequency of this object in collection A. * @param obj the object * @return the frequency of the object in collection A */ public int freqA(final Object obj) { return getFreq(obj, cardinalityA); } /** * Returns the frequency of this object in collection B. * @param obj the object * @return the frequency of the object in collection B */ public int freqB(final Object obj) { return getFreq(obj, cardinalityB); } private final int getFreq(final Object obj, final Map freqMap) { final Integer count = freqMap.get(obj); if (count != null) { return count.intValue(); } return 0; } } /** * Helper class for set-related operations, e.g. union, subtract, intersection. * @param the element type */ private static class SetOperationCardinalityHelper extends CardinalityHelper implements Iterable { /** Contains the unique elements of the two collections. */ private final Set elements; /** Output collection. */ private final List newList; /** * Create a new set operation helper from the two collections. * @param a the first collection * @param b the second collection */ public SetOperationCardinalityHelper(final Iterable a, final Iterable b) { super(a, b); elements = new HashSet(); addAll(elements, a); addAll(elements, b); // the resulting list must contain at least each unique element, but may grow newList = new ArrayList(elements.size()); } public Iterator iterator() { return elements.iterator(); } /** * Add the object {@code count} times to the result collection. * @param obj the object to add * @param count the count */ public void setCardinality(final O obj, final int count) { for (int i = 0; i < count; i++) { newList.add(obj); } } /** * Returns the resulting collection. * @return the result */ public Collection list() { return newList; } } /** * An empty unmodifiable collection. * The JDK provides empty Set and List implementations which could be used for * this purpose. However they could be cast to Set or List which might be * undesirable. This implementation only implements Collection. */ @SuppressWarnings("rawtypes") // we deliberately use the raw type here public static final Collection EMPTY_COLLECTION = UnmodifiableCollection.unmodifiableCollection(new ArrayList()); /** * CollectionUtils should not normally be instantiated. */ private CollectionUtils() {} /** * Returns the immutable EMPTY_COLLECTION with generic type safety. * * @see #EMPTY_COLLECTION * @since 4.0 * @param the element type * @return immutable empty collection */ @SuppressWarnings("unchecked") // OK, empty collection is compatible with any type public static Collection emptyCollection() { return EMPTY_COLLECTION; } /** * Returns an immutable empty collection if the argument is null, * or the argument itself otherwise. * * @param the element type * @param collection the collection, possibly null * @return an empty collection if the argument is null */ @SuppressWarnings("unchecked") // OK, empty collection is compatible with any type public static Collection emptyIfNull(final Collection collection) { return collection == null ? EMPTY_COLLECTION : collection; } /** * Returns a {@link Collection} containing the union of the given * {@link Iterable}s. *

* The cardinality of each element in the returned {@link Collection} will * be equal to the maximum of the cardinality of that element in the two * given {@link Iterable}s. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the union of the two collections * @see Collection#addAll */ public static Collection union(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.max(obj)); } return helper.list(); } /** * Returns a {@link Collection} containing the intersection of the given * {@link Iterable}s. *

* The cardinality of each element in the returned {@link Collection} will * be equal to the minimum of the cardinality of that element in the two * given {@link Iterable}s. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the intersection of the two collections * @see Collection#retainAll * @see #containsAny */ public static Collection intersection(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.min(obj)); } return helper.list(); } /** * Returns a {@link Collection} containing the exclusive disjunction * (symmetric difference) of the given {@link Iterable}s. *

* The cardinality of each element e in the returned * {@link Collection} will be equal to * max(cardinality(e,a),cardinality(e,b)) - min(cardinality(e,a), * cardinality(e,b)). *

* This is equivalent to * {@link #subtract subtract}({@link #union union(a,b)},{@link #intersection intersection(a,b)}) * or * {@link #union union}({@link #subtract subtract(a,b)},{@link #subtract subtract(b,a)}). * @param a the first collection, must not be null * @param b the second collection, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return the symmetric difference of the two collections */ public static Collection disjunction(final Iterable a, final Iterable b) { final SetOperationCardinalityHelper helper = new SetOperationCardinalityHelper(a, b); for (final O obj : helper) { helper.setCardinality(obj, helper.max(obj) - helper.min(obj)); } return helper.list(); } /** * Returns a new {@link Collection} containing a - b. * The cardinality of each element e in the returned {@link Collection} * will be the cardinality of e in a minus the cardinality * of e in b, or zero, whichever is greater. * * @param a the collection to subtract from, must not be null * @param b the collection to subtract, must not be null * @param the generic type that is able to represent the types contained * in both input collections. * @return a new collection with the results * @see Collection#removeAll */ public static Collection subtract(final Iterable a, final Iterable b) { final Predicate p = TruePredicate.truePredicate(); return subtract(a, b, p); } /** * Returns a new {@link Collection} containing a minus a subset of * b. Only the elements of b that satisfy the predicate * condition, p are subtracted from a. * *

The cardinality of each element e in the returned {@link Collection} * that satisfies the predicate condition will be the cardinality of e in a * minus the cardinality of e in b, or zero, whichever is greater.

*

The cardinality of each element e in the returned {@link Collection} that does not * satisfy the predicate condition will be equal to the cardinality of e in a.

* * @param a the collection to subtract from, must not be null * @param b the collection to subtract, must not be null * @param p the condition used to determine which elements of b are * subtracted. * @param the generic type that is able to represent the types contained * in both input collections. * @return a new collection with the results * @since 4.0 * @see Collection#removeAll */ public static Collection subtract(final Iterable a, final Iterable b, final Predicate p) { final ArrayList list = new ArrayList(); final HashBag bag = new HashBag(); for (final O element : b) { if (p.evaluate(element)) { bag.add(element); } } for (final O element : a) { if (!bag.remove(element, 1)) { list.add(element); } } return list; } /** * Returns true iff all elements of {@code coll2} are also contained * in {@code coll1}. The cardinality of values in {@code coll2} is not taken into account, * which is the same behavior as {@link Collection#containsAll(Collection)}. *

* In other words, this method returns true iff the * {@link #intersection} of coll1 and coll2 has the same cardinality as * the set of unique values from {@code coll2}. In case {@code coll2} is empty, {@code true} * will be returned. *

* This method is intended as a replacement for {@link Collection#containsAll(Collection)} * with a guaranteed runtime complexity of {@code O(n + m)}. Depending on the type of * {@link Collection} provided, this method will be much faster than calling * {@link Collection#containsAll(Collection)} instead, though this will come at the * cost of an additional space complexity O(n). * * @param coll1 the first collection, must not be null * @param coll2 the second collection, must not be null * @return true iff the intersection of the collections has the same cardinality * as the set of unique elements from the second collection * @since 4.0 */ public static boolean containsAll(final Collection coll1, final Collection coll2) { if (coll2.isEmpty()) { return true; } else { final Iterator it = coll1.iterator(); final Set elementsAlreadySeen = new HashSet(); for (final Object nextElement : coll2) { if (elementsAlreadySeen.contains(nextElement)) { continue; } boolean foundCurrentElement = false; while (it.hasNext()) { final Object p = it.next(); elementsAlreadySeen.add(p); if (nextElement == null ? p == null : nextElement.equals(p)) { foundCurrentElement = true; break; } } if (foundCurrentElement) { continue; } else { return false; } } return true; } } /** * Returns true iff at least one element is in both collections. *

* In other words, this method returns true iff the * {@link #intersection} of coll1 and coll2 is not empty. * * @param coll1 the first collection, must not be null * @param coll2 the second collection, must not be null * @return true iff the intersection of the collections is non-empty * @since 2.1 * @see #intersection */ public static boolean containsAny(final Collection coll1, final Collection coll2) { if (coll1.size() < coll2.size()) { for (final Object aColl1 : coll1) { if (coll2.contains(aColl1)) { return true; } } } else { for (final Object aColl2 : coll2) { if (coll1.contains(aColl2)) { return true; } } } return false; } /** * Returns a {@link Map} mapping each unique element in the given * {@link Collection} to an {@link Integer} representing the number * of occurrences of that element in the {@link Collection}. *

* Only those elements present in the collection will appear as * keys in the map. * * @param the type of object in the returned {@link Map}. This is a super type of . * @param coll the collection to get the cardinality map for, must not be null * @return the populated cardinality map */ public static Map getCardinalityMap(final Iterable coll) { final Map count = new HashMap(); for (final O obj : coll) { final Integer c = count.get(obj); if (c == null) { count.put(obj, Integer.valueOf(1)); } else { count.put(obj, Integer.valueOf(c.intValue() + 1)); } } return count; } /** * Returns true iff a is a sub-collection of b, * that is, iff the cardinality of e in a is less than or * equal to the cardinality of e in b, for each element e * in a. * * @param a the first (sub?) collection, must not be null * @param b the second (super?) collection, must not be null * @return true iff a is a sub-collection of b * @see #isProperSubCollection * @see Collection#containsAll */ public static boolean isSubCollection(final Collection a, final Collection b) { final CardinalityHelper helper = new CardinalityHelper(a, b); for (final Object obj : a) { if (helper.freqA(obj) > helper.freqB(obj)) { return false; } } return true; } /** * Returns true iff a is a proper sub-collection of b, * that is, iff the cardinality of e in a is less * than or equal to the cardinality of e in b, * for each element e in a, and there is at least one * element f such that the cardinality of f in b * is strictly greater than the cardinality of f in a. *

* The implementation assumes *

    *
  • a.size() and b.size() represent the * total cardinality of a and b, resp.
  • *
  • a.size() < Integer.MAXVALUE
  • *
* * @param a the first (sub?) collection, must not be null * @param b the second (super?) collection, must not be null * @return true iff a is a proper sub-collection of b * @see #isSubCollection * @see Collection#containsAll */ public static boolean isProperSubCollection(final Collection a, final Collection b) { return a.size() < b.size() && CollectionUtils.isSubCollection(a, b); } /** * Returns true iff the given {@link Collection}s contain * exactly the same elements with exactly the same cardinalities. *

* That is, iff the cardinality of e in a is * equal to the cardinality of e in b, * for each element e in a or b. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @return true iff the collections contain the same elements with the same cardinalities. */ public static boolean isEqualCollection(final Collection a, final Collection b) { if(a.size() != b.size()) { return false; } final CardinalityHelper helper = new CardinalityHelper(a, b); if(helper.cardinalityA.size() != helper.cardinalityB.size()) { return false; } for( final Object obj : helper.cardinalityA.keySet()) { if(helper.freqA(obj) != helper.freqB(obj)) { return false; } } return true; } /** * Returns true iff the given {@link Collection}s contain * exactly the same elements with exactly the same cardinalities. *

* That is, iff the cardinality of e in a is * equal to the cardinality of e in b, * for each element e in a or b. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @param equator the Equator used for testing equality * @return true iff the collections contain the same elements with the same cardinalities. * @throws IllegalArgumentException if the equator is null * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) // we don't know the types due to wildcards in the signature public static boolean isEqualCollection(final Collection a, final Collection b, final Equator equator) { if (equator == null) { throw new IllegalArgumentException("equator may not be null"); } if(a.size() != b.size()) { return false; } final Transformer transformer = new Transformer() { public EquatorWrapper transform(final Object input) { return new EquatorWrapper(equator, input); } }; return isEqualCollection(collect(a, transformer), collect(b, transformer)); } /** * Wraps another object and uses the provided Equator to implement * {@link #equals(Object)} and {@link #hashCode()}. *

* This class can be used to store objects into a Map. * * @param the element type * @since 4.0 */ private static class EquatorWrapper { private final Equator equator; private final O object; public EquatorWrapper(final Equator equator, final O object) { this.equator = equator; this.object = object; } public O getObject() { return object; } @Override public boolean equals(final Object obj) { if (!(obj instanceof EquatorWrapper)) { return false; } @SuppressWarnings("unchecked") final EquatorWrapper otherObj = (EquatorWrapper) obj; return equator.equate(object, otherObj.getObject()); } @Override public int hashCode() { return equator.hash(object); } } /** * Returns the number of occurrences of obj in coll. * * @param obj the object to find the cardinality of * @param coll the {@link Iterable} to search * @param the type of object that the {@link Iterable} may contain. * @return the the number of occurrences of obj in coll */ public static int cardinality(final O obj, final Iterable coll) { if (coll instanceof Set) { return ((Set) coll).contains(obj) ? 1 : 0; } if (coll instanceof Bag) { return ((Bag) coll).getCount(obj); } int count = 0; if (obj == null) { for (final Object element : coll) { if (element == null) { count++; } } } else { for (final Object element : coll) { if (obj.equals(element)) { count++; } } } return count; } /** * Finds the first element in the given collection which matches the given predicate. *

* If the input collection or predicate is null, or no element of the collection * matches the predicate, null is returned. * * @param the type of object the {@link Iterable} contains * @param collection the collection to search, may be null * @param predicate the predicate to use, may be null * @return the first element of the collection which matches the predicate or null if none could be found */ public static T find(final Iterable collection, final Predicate predicate) { if (collection != null && predicate != null) { for (final T item : collection) { if (predicate.evaluate(item)) { return item; } } } return null; } /** * Executes the given closure on each element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param the closure type * @param collection the collection to get the input from, may be null * @param closure the closure to perform, may be null * @return closure */ public static > C forAllDo(final Iterable collection, final C closure) { if (collection != null && closure != null) { for (final T element : collection) { closure.execute(element); } } return closure; } /** * Executes the given closure on each element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterator} contains * @param the closure type * @param iterator the iterator to get the input from, may be null * @param closure the closure to perform, may be null * @return closure * @since 4.0 */ public static > C forAllDo(final Iterator iterator, final C closure) { if (iterator != null && closure != null) { while (iterator.hasNext()) { closure.execute(iterator.next()); } } return closure; } /** * Executes the given closure on each but the last element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param the closure type * @param collection the collection to get the input from, may be null * @param closure the closure to perform, may be null * @return the last element in the collection, or null if either collection or closure is null * @since 4.0 */ public static > T forAllButLastDo(final Iterable collection, final C closure) { return collection != null && closure != null ? forAllButLastDo(collection.iterator(), closure) : null; } /** * Executes the given closure on each but the last element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param the type of object the {@link Collection} contains * @param the closure type * @param iterator the iterator to get the input from, may be null * @param closure the closure to perform, may be null * @return the last element in the collection, or null if either iterator or closure is null * @since 4.0 */ public static > T forAllButLastDo(final Iterator iterator, final C closure) { if (iterator != null && closure != null) { while (iterator.hasNext()) { final T element = iterator.next(); if (iterator.hasNext()) { closure.execute(element); } else { return element; } } } return null; } /** * Filter the collection by applying a Predicate to each element. If the * predicate returns false, remove the element. *

* If the input collection or predicate is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param collection the collection to get the input from, may be null * @param predicate the predicate to use as a filter, may be null * @return true if the collection is modified by this call, false otherwise. */ public static boolean filter(final Iterable collection, final Predicate predicate) { boolean result = false; if (collection != null && predicate != null) { for (final Iterator it = collection.iterator(); it.hasNext();) { if (!predicate.evaluate(it.next())) { it.remove(); result = true; } } } return result; } /** * Filter the collection by applying a Predicate to each element. If the * predicate returns true, remove the element. *

* This is equivalent to

filter(collection, PredicateUtils.notPredicate(predicate))
* if predicate is != null. *

* If the input collection or predicate is null, there is no change made. * * @param the type of object the {@link Iterable} contains * @param collection the collection to get the input from, may be null * @param predicate the predicate to use as a filter, may be null * @return true if the collection is modified by this call, false otherwise. */ public static boolean filterInverse(final Iterable collection, final Predicate predicate) { return filter(collection, predicate == null ? null : PredicateUtils.notPredicate(predicate)); } /** * Transform the collection by applying a Transformer to each element. *

* If the input collection or transformer is null, there is no change made. *

* This routine is best for Lists, for which set() is used to do the * transformations "in place." For other Collections, clear() and addAll() * are used to replace elements. *

* If the input collection controls its input, such as a Set, and the * Transformer creates duplicates (or are otherwise invalid), the collection * may reduce in size due to calling this method. * * @param the type of object the {@link Collection} contains * @param collection the {@link Collection} to get the input from, may be null * @param transformer the transformer to perform, may be null */ public static void transform(final Collection collection, final Transformer transformer) { if (collection != null && transformer != null) { if (collection instanceof List) { final List list = (List) collection; for (final ListIterator it = list.listIterator(); it.hasNext();) { it.set(transformer.transform(it.next())); } } else { final Collection resultCollection = collect(collection, transformer); collection.clear(); collection.addAll(resultCollection); } } } /** * Counts the number of elements in the input collection that match the * predicate. *

* A null collection or predicate matches no elements. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return the number of matches for the predicate in the collection */ public static int countMatches(final Iterable input, final Predicate predicate) { int count = 0; if (input != null && predicate != null) { for (final C o : input) { if (predicate.evaluate(o)) { count++; } } } return count; } /** * Answers true if a predicate is true for at least one element of a * collection. *

* A null collection or predicate returns false. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return true if at least one element of the collection matches the predicate */ public static boolean exists(final Iterable input, final Predicate predicate) { if (input != null && predicate != null) { for (final C o : input) { if (predicate.evaluate(o)) { return true; } } } return false; } /** * Answers true if a predicate is true for every element of a * collection. *

* A null predicate returns false.
* A null or empty collection returns true. * * @param the type of object the {@link Iterable} contains * @param input the {@link Iterable} to get the input from, may be null * @param predicate the predicate to use, may be null * @return true if every element of the collection matches the predicate or if the * collection is empty, false otherwise * @since 4.0 */ public static boolean matchesAll(final Iterable input, final Predicate predicate) { if (predicate == null) { return false; } if (input != null) { for (final C o : input) { if (!predicate.evaluate(o)) { return false; } } } return true; } /** * Selects all elements from input collection which match the given * predicate into an output collection. *

* A null predicate matches no elements. * * @param the type of object the {@link Iterable} contains * @param inputCollection the collection to get the input from, may not be null * @param predicate the predicate to use, may be null * @return the elements matching the predicate (new list) * @throws NullPointerException if the input collection is null */ public static Collection select(final Iterable inputCollection, final Predicate predicate) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return select(inputCollection, predicate, answer); } /** * Selects all elements from input collection which match the given * predicate and adds them to outputCollection. *

* If the input collection or predicate is null, there is no change to the * output collection. * * @param the type of object the {@link Iterable} contains * @param the type of the output {@link Collection} * @param inputCollection the collection to get the input from, may be null * @param predicate the predicate to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and predicate or not null * @return the outputCollection */ public static > R select(final Iterable inputCollection, final Predicate predicate, final R outputCollection) { if (inputCollection != null && predicate != null) { for (final O item : inputCollection) { if (predicate.evaluate(item)) { outputCollection.add(item); } } } return outputCollection; } /** * Selects all elements from inputCollection which don't match the given * predicate into an output collection. *

* If the input predicate is null, the result is an empty * list. * * @param the type of object the {@link Iterable} contains * @param inputCollection the collection to get the input from, may not be null * @param predicate the predicate to use, may be null * @return the elements not matching the predicate (new list) * @throws NullPointerException if the input collection is null */ public static Collection selectRejected(final Iterable inputCollection, final Predicate predicate) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return selectRejected(inputCollection, predicate, answer); } /** * Selects all elements from inputCollection which don't match the given * predicate and adds them to outputCollection. *

* If the input predicate is null, no elements are added to * outputCollection. * * @param the type of object the {@link Iterable} contains * @param the type of the output {@link Collection} * @param inputCollection the collection to get the input from, may be null * @param predicate the predicate to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and predicate or not null * @return outputCollection */ public static > R selectRejected(final Iterable inputCollection, final Predicate predicate, final R outputCollection) { if (inputCollection != null && predicate != null) { for (final O item : inputCollection) { if (!predicate.evaluate(item)) { outputCollection.add(item); } } } return outputCollection; } /** * Returns a new Collection consisting of the elements of inputCollection * transformed by the given transformer. *

* If the input transformer is null, the result is an empty list. * * @param the type of object in the input collection * @param the type of object in the output collection * @param inputCollection the collection to get the input from, may not be null * @param transformer the transformer to use, may be null * @return the transformed result (new list) * @throws NullPointerException if the input collection is null */ public static Collection collect(final Iterable inputCollection, final Transformer transformer) { final Collection answer = inputCollection instanceof Collection ? new ArrayList(((Collection) inputCollection).size()) : new ArrayList(); return collect(inputCollection, transformer, answer); } /** * Transforms all elements from the inputIterator with the given transformer * and adds them to the outputCollection. *

* If the input iterator or transformer is null, the result is an empty * list. * * @param inputIterator the iterator to get the input from, may be null * @param transformer the transformer to use, may be null * @param the type of object in the input collection * @param the type of object in the output collection * @return the transformed result (new list) */ public static Collection collect(final Iterator inputIterator, final Transformer transformer) { return collect(inputIterator, transformer, new ArrayList()); } /** * Transforms all elements from inputCollection with the given transformer * and adds them to the outputCollection. *

* If the input collection or transformer is null, there is no change to the * output collection. * * @param the type of object in the input collection * @param the type of object in the output collection * @param the output type of the transformer - this extends O. * @param inputCollection the collection to get the input from, may be null * @param transformer the transformer to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and transformer are not null * @return the outputCollection with the transformed input added * @throws NullPointerException if the output collection is null and both, inputCollection and * transformer are not null */ public static > R collect(final Iterable inputCollection, final Transformer transformer, final R outputCollection) { if (inputCollection != null) { return collect(inputCollection.iterator(), transformer, outputCollection); } return outputCollection; } /** * Transforms all elements from the inputIterator with the given transformer * and adds them to the outputCollection. *

* If the input iterator or transformer is null, there is no change to the * output collection. * * @param inputIterator the iterator to get the input from, may be null * @param transformer the transformer to use, may be null * @param outputCollection the collection to output into, may not be null if the inputCollection * and transformer are not null * @param the type of object in the input collection * @param the type of object in the output collection * @param the output type of the transformer - this extends O. * @return the outputCollection with the transformed input added * @throws NullPointerException if the output collection is null and both, inputCollection and * transformer are not null */ public static > R collect(final Iterator inputIterator, final Transformer transformer, final R outputCollection) { if (inputIterator != null && transformer != null) { while (inputIterator.hasNext()) { final I item = inputIterator.next(); final O value = transformer.transform(item); outputCollection.add(value); } } return outputCollection; } //----------------------------------------------------------------------- /** * Adds an element to the collection unless the element is null. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param object the object to add, if null it will not be added * @return true if the collection changed * @throws NullPointerException if the collection is null * @since 3.2 */ public static boolean addIgnoreNull(final Collection collection, final T object) { if (collection == null) { throw new NullPointerException("The collection must not be null"); } return object != null && collection.add(object); } /** * Adds all elements in the {@link Iterable} to the given collection. If the * {@link Iterable} is a {@link Collection} then it is cast and will be * added using {@link Collection#addAll(Collection)} instead of iterating. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param iterable the iterable of elements to add, must not be null * @return a boolean indicating whether the collection has changed or not. * @throws NullPointerException if the collection or iterator is null */ public static boolean addAll(final Collection collection, final Iterable iterable) { if (iterable instanceof Collection) { return collection.addAll((Collection) iterable); } return addAll(collection, iterable.iterator()); } /** * Adds all elements in the iteration to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param iterator the iterator of elements to add, must not be null * @return a boolean indicating whether the collection has changed or not. * @throws NullPointerException if the collection or iterator is null */ public static boolean addAll(final Collection collection, final Iterator iterator) { boolean changed = false; while (iterator.hasNext()) { changed |= collection.add(iterator.next()); } return changed; } /** * Adds all elements in the enumeration to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param enumeration the enumeration of elements to add, must not be null * @return {@code true} if the collections was changed, {@code false} otherwise * @throws NullPointerException if the collection or enumeration is null */ public static boolean addAll(final Collection collection, final Enumeration enumeration) { boolean changed = false; while (enumeration.hasMoreElements()) { changed |= collection.add(enumeration.nextElement()); } return changed; } /** * Adds all elements in the array to the given collection. * * @param the type of object the {@link Collection} contains * @param collection the collection to add to, must not be null * @param elements the array of elements to add, must not be null * @return {@code true} if the collection was changed, {@code false} otherwise * @throws NullPointerException if the collection or array is null */ public static boolean addAll(final Collection collection, final C[] elements) { boolean changed = false; for (final C element : elements) { changed |= collection.add(element); } return changed; } /** * Returns the index-th value in {@link Iterator}, throwing * IndexOutOfBoundsException if there is no such element. *

* The Iterator is advanced to index (or to the end, if * index exceeds the number of entries) as a side effect of this method. * * @param iterator the iterator to get a value from * @param index the index to get * @param the type of object in the {@link Iterator} * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid * @throws IllegalArgumentException if the object type is invalid */ public static T get(final Iterator iterator, final int index) { int i = index; checkIndexBounds(i); while (iterator.hasNext()) { i--; if (i == -1) { return iterator.next(); } iterator.next(); } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } /** * Ensures an index is not negative. * @param index the index to check. * @throws IndexOutOfBoundsException if the index is negative. */ private static void checkIndexBounds(final int index) { if (index < 0) { throw new IndexOutOfBoundsException("Index cannot be negative: " + index); } } /** * Returns the index-th value in the iterable's {@link Iterator}, throwing * IndexOutOfBoundsException if there is no such element. *

* If the {@link Iterable} is a {@link List}, then it will use {@link List#get(int)}. * * @param iterable the {@link Iterable} to get a value from * @param index the index to get * @param the type of object in the {@link Iterable}. * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid */ public static T get(final Iterable iterable, final int index) { checkIndexBounds(index); if (iterable instanceof List) { return ((List) iterable).get(index); } return get(iterable.iterator(), index); } /** * Returns the index-th value in object, throwing * IndexOutOfBoundsException if there is no such element or * IllegalArgumentException if object is not an * instance of one of the supported types. *

* The supported types, and associated semantics are: *

    *
  • Map -- the value returned is the Map.Entry in position * index in the map's entrySet iterator, * if there is such an entry.
  • *
  • List -- this method is equivalent to the list's get method.
  • *
  • Array -- the index-th array entry is returned, * if there is such an entry; otherwise an IndexOutOfBoundsException * is thrown.
  • *
  • Collection -- the value returned is the index-th object * returned by the collection's default iterator, if there is such an element.
  • *
  • Iterator or Enumeration -- the value returned is the * index-th object in the Iterator/Enumeration, if there * is such an element. The Iterator/Enumeration is advanced to * index (or to the end, if index exceeds the * number of entries) as a side effect of this method.
  • *
* * @param object the object to get a value from * @param index the index to get * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid * @throws IllegalArgumentException if the object type is invalid */ public static Object get(final Object object, final int index) { int i = index; if (i < 0) { throw new IndexOutOfBoundsException("Index cannot be negative: " + i); } if (object instanceof Map) { final Map map = (Map) object; final Iterator iterator = map.entrySet().iterator(); return get(iterator, i); } else if (object instanceof Object[]) { return ((Object[]) object)[i]; } else if (object instanceof Iterator) { final Iterator it = (Iterator) object; while (it.hasNext()) { i--; if (i == -1) { return it.next(); } it.next(); } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } else if (object instanceof Collection) { final Iterator iterator = ((Collection) object).iterator(); return get(iterator, i); } else if (object instanceof Enumeration) { final Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { i--; if (i == -1) { return it.nextElement(); } else { it.nextElement(); } } throw new IndexOutOfBoundsException("Entry does not exist: " + i); } else if (object == null) { throw new IllegalArgumentException("Unsupported object type: null"); } else { try { return Array.get(object, i); } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } /** * Returns the index-th Map.Entry in the map's entrySet, * throwing IndexOutOfBoundsException if there is no such element. * * @param the key type in the {@link Map} * @param the key type in the {@link Map} * @param map the object to get a value from * @param index the index to get * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid */ public static Map.Entry get(final Map map, final int index) { checkIndexBounds(index); return get(map.entrySet(), index); } /** * Gets the size of the collection/iterator specified. *

* This method can handles objects as follows *

    *
  • Collection - the collection size *
  • Map - the map size *
  • Array - the array size *
  • Iterator - the number of elements remaining in the iterator *
  • Enumeration - the number of elements remaining in the enumeration *
* * @param object the object to get the size of, may be null * @return the size of the specified collection or 0 if the object was null * @throws IllegalArgumentException thrown if object is not recognised * @since 3.1 */ public static int size(final Object object) { if (object == null) { return 0; } int total = 0; if (object instanceof Map) { total = ((Map) object).size(); } else if (object instanceof Collection) { total = ((Collection) object).size(); } else if (object instanceof Object[]) { total = ((Object[]) object).length; } else if (object instanceof Iterator) { final Iterator it = (Iterator) object; while (it.hasNext()) { total++; it.next(); } } else if (object instanceof Enumeration) { final Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { total++; it.nextElement(); } } else { try { total = Array.getLength(object); } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } return total; } /** * Checks if the specified collection/array/iterator is empty. *

* This method can handles objects as follows *

    *
  • Collection - via collection isEmpty *
  • Map - via map isEmpty *
  • Array - using array size *
  • Iterator - via hasNext *
  • Enumeration - via hasMoreElements *
*

* Note: This method is named to avoid clashing with * {@link #isEmpty(Collection)}. * * @param object the object to get the size of, may be null * @return true if empty or null * @throws IllegalArgumentException thrown if object is not recognised * @since 3.2 */ public static boolean sizeIsEmpty(final Object object) { if (object == null) { return true; } else if (object instanceof Collection) { return ((Collection) object).isEmpty(); } else if (object instanceof Map) { return ((Map) object).isEmpty(); } else if (object instanceof Object[]) { return ((Object[]) object).length == 0; } else if (object instanceof Iterator) { return ((Iterator) object).hasNext() == false; } else if (object instanceof Enumeration) { return ((Enumeration) object).hasMoreElements() == false; } else { try { return Array.getLength(object) == 0; } catch (final IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } //----------------------------------------------------------------------- /** * Null-safe check if the specified collection is empty. *

* Null returns true. * * @param coll the collection to check, may be null * @return true if empty or null * @since 3.2 */ public static boolean isEmpty(final Collection coll) { return coll == null || coll.isEmpty(); } /** * Null-safe check if the specified collection is not empty. *

* Null returns false. * * @param coll the collection to check, may be null * @return true if non-null and non-empty * @since 3.2 */ public static boolean isNotEmpty(final Collection coll) { return !isEmpty(coll); } //----------------------------------------------------------------------- /** * Reverses the order of the given array. * * @param array the array to reverse */ public static void reverseArray(final Object[] array) { int i = 0; int j = array.length - 1; Object tmp; while (j > i) { tmp = array[j]; array[j] = array[i]; array[i] = tmp; j--; i++; } } /** * Returns true if no more elements can be added to the Collection. *

* This method uses the {@link BoundedCollection} interface to determine the * full status. If the collection does not implement this interface then * false is returned. *

* The collection does not have to implement this interface directly. * If the collection has been decorated using the decorators subpackage * then these will be removed to access the BoundedCollection. * * @param coll the collection to check * @return true if the BoundedCollection is full * @throws NullPointerException if the collection is null */ public static boolean isFull(final Collection coll) { if (coll == null) { throw new NullPointerException("The collection must not be null"); } if (coll instanceof BoundedCollection) { return ((BoundedCollection) coll).isFull(); } try { final BoundedCollection bcoll = UnmodifiableBoundedCollection.unmodifiableBoundedCollection(coll); return bcoll.isFull(); } catch (final IllegalArgumentException ex) { return false; } } /** * Get the maximum number of elements that the Collection can contain. *

* This method uses the {@link BoundedCollection} interface to determine the * maximum size. If the collection does not implement this interface then * -1 is returned. *

* The collection does not have to implement this interface directly. * If the collection has been decorated using the decorators subpackage * then these will be removed to access the BoundedCollection. * * @param coll the collection to check * @return the maximum size of the BoundedCollection, -1 if no maximum size * @throws NullPointerException if the collection is null */ public static int maxSize(final Collection coll) { if (coll == null) { throw new NullPointerException("The collection must not be null"); } if (coll instanceof BoundedCollection) { return ((BoundedCollection) coll).maxSize(); } try { final BoundedCollection bcoll = UnmodifiableBoundedCollection.unmodifiableBoundedCollection(coll); return bcoll.maxSize(); } catch (final IllegalArgumentException ex) { return -1; } } //----------------------------------------------------------------------- /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the natural ordering of the elements is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection is null * @since 4.0 */ public static > List collate(Iterable a, Iterable b) { return collate(a, b, ComparatorUtils.naturalComparator(), true); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the natural ordering of the elements is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param includeDuplicates if {@code true} duplicate elements will be retained, otherwise * they will be removed in the output collection * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection is null * @since 4.0 */ public static > List collate(final Iterable a, final Iterable b, final boolean includeDuplicates) { return collate(a, b, ComparatorUtils.naturalComparator(), includeDuplicates); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the ordering of the elements according to Comparator c is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param c the comparator to use for the merge. * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection or the comparator is null * @since 4.0 */ public static List collate(final Iterable a, final Iterable b, final Comparator c) { return collate(a, b, c, true); } /** * Merges two sorted Collections, a and b, into a single, sorted List * such that the ordering of the elements according to Comparator c is retained. *

* Uses the standard O(n) merge algorithm for combining two sorted lists. * * @param the element type * @param a the first collection, must not be null * @param b the second collection, must not be null * @param c the comparator to use for the merge. * @param includeDuplicates if {@code true} duplicate elements will be retained, otherwise * they will be removed in the output collection * @return a new sorted List, containing the elements of Collection a and b * @throws IllegalArgumentException if either collection or the comparator is null * @since 4.0 */ public static List collate(final Iterable a, final Iterable b, final Comparator c, final boolean includeDuplicates) { if (a == null || b == null) { throw new IllegalArgumentException("The collections must not be null"); } if (c == null) { throw new IllegalArgumentException("The comparator must not be null"); } // if both Iterables are a Collection, we can estimate the size final int totalSize = a instanceof Collection && b instanceof Collection ? Math.max(1, ((Collection) a).size() + ((Collection) b).size()) : 10; final Iterator iterator = new CollatingIterator(c, a.iterator(), b.iterator()); if (includeDuplicates) { return IteratorUtils.toList(iterator, totalSize); } else { final ArrayList mergedList = new ArrayList(totalSize); O lastItem = null; while (iterator.hasNext()) { final O item = iterator.next(); if (lastItem == null || !lastItem.equals(item)) { mergedList.add(item); } lastItem = item; } mergedList.trimToSize(); return mergedList; } } //----------------------------------------------------------------------- /** * Returns a {@link Collection} of all the permutations of the input collection. *

* NOTE: the number of permutations of a given collection is equal to n!, where * n is the size of the collection. Thus, the resulting collection will become * very large for collections > 10 (e.g. 10! = 3628800, 15! = 1307674368000). *

* For larger collections it is advised to use a {@link PermutationIterator} to * iterate over all permutations. * * @see PermutationIterator * * @param the element type * @param collection the collection to create permutations for, may not be null * @return an unordered collection of all permutations of the input collection * @throws NullPointerException if collection is null * @since 4.0 */ public static Collection> permutations(final Collection collection) { final PermutationIterator it = new PermutationIterator(collection); final Collection> result = new LinkedList>(); while (it.hasNext()) { result.add(it.next()); } return result; } //----------------------------------------------------------------------- /** * Returns a collection containing all the elements in collection * that are also in retain. The cardinality of an element e * in the returned collection is the same as the cardinality of e * in collection unless retain does not contain e, in which * case the cardinality is zero. This method is useful if you do not wish to modify * the collection c and thus cannot call c.retainAll(retain);. * * @param the type of object the {@link Collection} contains * @param collection the collection whose contents are the target of the #retailAll operation * @param retain the collection containing the elements to be retained in the returned collection * @return a Collection containing all the elements of collection * that occur at least once in retain. * @throws NullPointerException if either parameter is null * @since 3.2 */ public static Collection retainAll(final Collection collection, final Collection retain) { return ListUtils.retainAll(collection, retain); } /** * Removes the elements in remove from collection. That is, this * method returns a collection containing all the elements in c * that are not in remove. The cardinality of an element e * in the returned collection is the same as the cardinality of e * in collection unless remove contains e, in which * case the cardinality is zero. This method is useful if you do not wish to modify * the collection c and thus cannot call collection.removeAll(remove);. * * @param the type of object the {@link Collection} contains * @param collection the collection from which items are removed (in the returned collection) * @param remove the items to be removed from the returned collection * @return a Collection containing all the elements of collection except * any elements that also occur in remove. * @throws NullPointerException if either parameter is null * @since 4.0 (method existed in 3.2 but was completely broken) */ public static Collection removeAll(final Collection collection, final Collection remove) { return ListUtils.removeAll(collection, remove); } //----------------------------------------------------------------------- /** * Returns a synchronized collection backed by the given collection. *

* You must manually synchronize on the returned buffer's iterator to * avoid non-deterministic behavior: * *

     * Collection c = CollectionUtils.synchronizedCollection(myCollection);
     * synchronized (c) {
     *     Iterator i = c.iterator();
     *     while (i.hasNext()) {
     *         process (i.next());
     *     }
     * }
     * 
* * This method uses the implementation in the decorators subpackage. * * @param the type of object the {@link Collection} contains * @param collection the collection to synchronize, must not be null * @return a synchronized collection backed by the given collection * @throws IllegalArgumentException if the collection is null */ public static Collection synchronizedCollection(final Collection collection) { return SynchronizedCollection.synchronizedCollection(collection); } /** * Returns an unmodifiable collection backed by the given collection. *

* This method uses the implementation in the decorators subpackage. * * @param the type of object the {@link Collection} contains * @param collection the collection to make unmodifiable, must not be null * @return an unmodifiable collection backed by the given collection * @throws IllegalArgumentException if the collection is null */ public static Collection unmodifiableCollection(final Collection collection) { return UnmodifiableCollection.unmodifiableCollection(collection); } /** * Returns a predicated (validating) collection backed by the given collection. *

* Only objects that pass the test in the given predicate can be added to the collection. * Trying to add an invalid object results in an IllegalArgumentException. * It is important not to use the original collection after invoking this method, * as it is a backdoor for adding invalid objects. * * @param collection the collection to predicate, must not be null * @param predicate the predicate for the collection, must not be null * @param the type of objects in the Collection. * @return a predicated collection backed by the given collection * @throws IllegalArgumentException if the Collection is null */ public static Collection predicatedCollection(final Collection collection, final Predicate predicate) { return PredicatedCollection.predicatedCollection(collection, predicate); } /** * Returns a transformed bag backed by the given collection. *

* Each object is passed through the transformer as it is added to the * Collection. It is important not to use the original collection after invoking this * method, as it is a backdoor for adding untransformed objects. *

* Existing entries in the specified collection will not be transformed. * If you want that behaviour, see {@link TransformedCollection#transformedCollection}. * * @param the type of object the {@link Collection} contains * @param collection the collection to predicate, must not be null * @param transformer the transformer for the collection, must not be null * @return a transformed collection backed by the given collection * @throws IllegalArgumentException if the Collection or Transformer is null */ public static Collection transformingCollection(final Collection collection, final Transformer transformer) { return TransformedCollection.transformingCollection(collection, transformer); } /** * Extract the lone element of the specified Collection. * @param collection type * @param collection to read * @return sole member of collection * @throws IllegalArgumentException if collection is null/empty or contains more than one element * @since 4.0 */ public static E extractSingleton(final Collection collection) { if (collection == null || collection.size() != 1) { throw new IllegalArgumentException("Can extract singleton only when collection size == 1"); } return collection.iterator().next(); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 22078; to = 21970.Please, attach your messages.log to new issue! WARNING [org.netbeans.TopSecurityManager]: use of system property netbeans.user has been obsoleted in favor of InstalledFileLocator/Places at org.netbeans.modules.java.source.TreeLoader.dumpCouplingAbort(TreeLoader.java:395) Coupling error: class file: file:/C:/Users/User06/AppData/Local/NetBeans/Cache/8.0-201403052200/index/s91/java/14/classes/org/apache/commons/collections4/MapUtils.sig source file: file:/C:/Users/User06/Projects/NetBeans/commons-collections4-4.0-src/src/main/java/org/apache/commons/collections4/MapUtils.java VARIABLE: x0 Coupling error: class file: file:/C:/Users/User06/AppData/Local/NetBeans/Cache/8.0-201403052200/index/s91/java/14/classes/org/apache/commons/collections4/MapUtils.sig source file: file:/C:/Users/User06/Projects/NetBeans/commons-collections4-4.0-src/src/main/java/org/apache/commons/collections4/MapUtils.java VARIABLE: x0 ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4; import java.io.PrintStream; import java.text.NumberFormat; import java.text.ParseException; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.ResourceBundle; import java.util.SortedMap; import java.util.TreeMap; import org.apache.commons.collections4.map.AbstractMapDecorator; import org.apache.commons.collections4.map.AbstractSortedMapDecorator; import org.apache.commons.collections4.map.FixedSizeMap; import org.apache.commons.collections4.map.FixedSizeSortedMap; import org.apache.commons.collections4.map.LazyMap; import org.apache.commons.collections4.map.LazySortedMap; import org.apache.commons.collections4.map.ListOrderedMap; import org.apache.commons.collections4.map.MultiValueMap; import org.apache.commons.collections4.map.PredicatedMap; import org.apache.commons.collections4.map.PredicatedSortedMap; import org.apache.commons.collections4.map.TransformedMap; import org.apache.commons.collections4.map.TransformedSortedMap; import org.apache.commons.collections4.map.UnmodifiableMap; import org.apache.commons.collections4.map.UnmodifiableSortedMap; /** * Provides utility methods and decorators for * {@link Map} and {@link SortedMap} instances. *

* It contains various type safe methods * as well as other useful features like deep copying. *

* It also provides the following decorators: * *

    *
  • {@link #fixedSizeMap(Map)} *
  • {@link #fixedSizeSortedMap(SortedMap)} *
  • {@link #lazyMap(Map,Factory)} *
  • {@link #lazyMap(Map,Transformer)} *
  • {@link #lazySortedMap(SortedMap,Factory)} *
  • {@link #lazySortedMap(SortedMap,Transformer)} *
  • {@link #predicatedMap(Map,Predicate,Predicate)} *
  • {@link #predicatedSortedMap(SortedMap,Predicate,Predicate)} *
  • {@link #transformedMap(Map, Transformer, Transformer)} *
  • {@link #transformedSortedMap(SortedMap, Transformer, Transformer)} *
  • {@link #multiValueMap( Map )} *
  • {@link #multiValueMap( Map, Class )} *
  • {@link #multiValueMap( Map, Factory )} *
* * @since 1.0 * @version $Id: MapUtils.java 1543964 2013-11-20 21:53:39Z tn $ */ public class MapUtils { /** * An empty unmodifiable sorted map. * This is not provided in the JDK. */ @SuppressWarnings("rawtypes") public static final SortedMap EMPTY_SORTED_MAP = UnmodifiableSortedMap.unmodifiableSortedMap(new TreeMap()); /** * String used to indent the verbose and debug Map prints. */ private static final String INDENT_STRING = " "; /** * MapUtils should not normally be instantiated. */ private MapUtils() {} // Type safe getters //------------------------------------------------------------------------- /** * Gets from a Map in a null-safe manner. * * @param the key type * @param the value type * @param map the map to use * @param key the key to look up * @return the value in the Map, null if null map input */ public static V getObject(final Map map, final K key) { if (map != null) { return map.get(key); } return null; } /** * Gets a String from a Map in a null-safe manner. *

* The String is obtained via toString. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a String, null if null map input */ public static String getString(final Map map, final K key) { if (map != null) { final Object answer = map.get(key); if (answer != null) { return answer.toString(); } } return null; } /** * Gets a Boolean from a Map in a null-safe manner. *

* If the value is a Boolean it is returned directly. * If the value is a String and it equals 'true' ignoring case * then true is returned, otherwise false. * If the value is a Number an integer zero value returns * false and non-zero returns true. * Otherwise, null is returned. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Boolean, null if null map input */ public static Boolean getBoolean(final Map map, final K key) { if (map != null) { final Object answer = map.get(key); if (answer != null) { if (answer instanceof Boolean) { return (Boolean) answer; } if (answer instanceof String) { return Boolean.valueOf((String) answer); } if (answer instanceof Number) { final Number n = (Number) answer; return n.intValue() != 0 ? Boolean.TRUE : Boolean.FALSE; } } } return null; } /** * Gets a Number from a Map in a null-safe manner. *

* If the value is a Number it is returned directly. * If the value is a String it is converted using * {@link NumberFormat#parse(String)} on the system default formatter * returning null if the conversion fails. * Otherwise, null is returned. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Number, null if null map input */ public static Number getNumber(final Map map, final K key) { if (map != null) { final Object answer = map.get(key); if (answer != null) { if (answer instanceof Number) { return (Number) answer; } if (answer instanceof String) { try { final String text = (String) answer; return NumberFormat.getInstance().parse(text); } catch (final ParseException e) { // NOPMD // failure means null is returned } } } } return null; } /** * Gets a Byte from a Map in a null-safe manner. *

* The Byte is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Byte, null if null map input */ public static Byte getByte(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Byte) { return (Byte) answer; } return Byte.valueOf(answer.byteValue()); } /** * Gets a Short from a Map in a null-safe manner. *

* The Short is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Short, null if null map input */ public static Short getShort(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Short) { return (Short) answer; } return Short.valueOf(answer.shortValue()); } /** * Gets a Integer from a Map in a null-safe manner. *

* The Integer is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Integer, null if null map input */ public static Integer getInteger(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Integer) { return (Integer) answer; } return Integer.valueOf(answer.intValue()); } /** * Gets a Long from a Map in a null-safe manner. *

* The Long is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Long, null if null map input */ public static Long getLong(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Long) { return (Long) answer; } return Long.valueOf(answer.longValue()); } /** * Gets a Float from a Map in a null-safe manner. *

* The Float is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Float, null if null map input */ public static Float getFloat(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Float) { return (Float) answer; } return Float.valueOf(answer.floatValue()); } /** * Gets a Double from a Map in a null-safe manner. *

* The Double is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Double, null if null map input */ public static Double getDouble(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Double) { return (Double) answer; } return Double.valueOf(answer.doubleValue()); } /** * Gets a Map from a Map in a null-safe manner. *

* If the value returned from the specified map is not a Map then * null is returned. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Map, null if null map input */ public static Map getMap(final Map map, final K key) { if (map != null) { final Object answer = map.get(key); if (answer != null && answer instanceof Map) { return (Map) answer; } } return null; } // Type safe getters with default values //------------------------------------------------------------------------- /** * Looks up the given key in the given map, converting null into the * given default value. * * @param the key type * @param the value type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null * @return the value in the map, or defaultValue if the original value * is null or the map is null */ public static V getObject(final Map map, final K key, final V defaultValue) { if (map != null) { final V answer = map.get(key); if (answer != null) { return answer; } } return defaultValue; } /** * Looks up the given key in the given map, converting the result into * a string, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a string, or defaultValue if the * original value is null, the map is null or the string conversion fails */ public static String getString(final Map map, final K key, final String defaultValue) { String answer = getString(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a boolean, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a boolean, or defaultValue if the * original value is null, the map is null or the boolean conversion fails */ public static Boolean getBoolean(final Map map, final K key, final Boolean defaultValue) { Boolean answer = getBoolean(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a number, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Number getNumber(final Map map, final K key, final Number defaultValue) { Number answer = getNumber(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a byte, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Byte getByte(final Map map, final K key, final Byte defaultValue) { Byte answer = getByte(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a short, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Short getShort(final Map map, final K key, final Short defaultValue) { Short answer = getShort(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * an integer, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Integer getInteger(final Map map, final K key, final Integer defaultValue) { Integer answer = getInteger(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a long, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Long getLong(final Map map, final K key, final Long defaultValue) { Long answer = getLong(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a float, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Float getFloat(final Map map, final K key, final Float defaultValue) { Float answer = getFloat(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a double, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Double getDouble(final Map map, final K key, final Double defaultValue) { Double answer = getDouble(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a map, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the map conversion fails */ public static Map getMap(final Map map, final K key, final Map defaultValue) { Map answer = getMap(map, key); if (answer == null) { answer = defaultValue; } return answer; } // Type safe primitive getters //------------------------------------------------------------------------- /** * Gets a boolean from a Map in a null-safe manner. *

* If the value is a Boolean its value is returned. * If the value is a String and it equals 'true' ignoring case * then true is returned, otherwise false. * If the value is a Number an integer zero value returns * false and non-zero returns true. * Otherwise, false is returned. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Boolean, false if null map input */ public static boolean getBooleanValue(final Map map, final K key) { return Boolean.TRUE.equals(getBoolean(map, key)); } /** * Gets a byte from a Map in a null-safe manner. *

* The byte is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a byte, 0 if null map input */ public static byte getByteValue(final Map map, final K key) { final Byte byteObject = getByte(map, key); if (byteObject == null) { return 0; } return byteObject.byteValue(); } /** * Gets a short from a Map in a null-safe manner. *

* The short is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a short, 0 if null map input */ public static short getShortValue(final Map map, final K key) { final Short shortObject = getShort(map, key); if (shortObject == null) { return 0; } return shortObject.shortValue(); } /** * Gets an int from a Map in a null-safe manner. *

* The int is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as an int, 0 if null map input */ public static int getIntValue(final Map map, final K key) { final Integer integerObject = getInteger(map, key); if (integerObject == null) { return 0; } return integerObject.intValue(); } /** * Gets a long from a Map in a null-safe manner. *

* The long is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a long, 0L if null map input */ public static long getLongValue(final Map map, final K key) { final Long longObject = getLong(map, key); if (longObject == null) { return 0L; } return longObject.longValue(); } /** * Gets a float from a Map in a null-safe manner. *

* The float is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a float, 0.0F if null map input */ public static float getFloatValue(final Map map, final K key) { final Float floatObject = getFloat(map, key); if (floatObject == null) { return 0f; } return floatObject.floatValue(); } /** * Gets a double from a Map in a null-safe manner. *

* The double is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a double, 0.0 if null map input */ public static double getDoubleValue(final Map map, final K key) { final Double doubleObject = getDouble(map, key); if (doubleObject == null) { return 0d; } return doubleObject.doubleValue(); } // Type safe primitive getters with default values //------------------------------------------------------------------------- /** * Gets a boolean from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* If the value is a Boolean its value is returned. * If the value is a String and it equals 'true' ignoring case * then true is returned, otherwise false. * If the value is a Number an integer zero value returns * false and non-zero returns true. * Otherwise, defaultValue is returned. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a Boolean, defaultValue if null map input */ public static boolean getBooleanValue(final Map map, final K key, final boolean defaultValue) { final Boolean booleanObject = getBoolean(map, key); if (booleanObject == null) { return defaultValue; } return booleanObject.booleanValue(); } /** * Gets a byte from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The byte is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a byte, defaultValue if null map input */ public static byte getByteValue(final Map map, final K key, final byte defaultValue) { final Byte byteObject = getByte(map, key); if (byteObject == null) { return defaultValue; } return byteObject.byteValue(); } /** * Gets a short from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The short is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a short, defaultValue if null map input */ public static short getShortValue(final Map map, final K key, final short defaultValue) { final Short shortObject = getShort(map, key); if (shortObject == null) { return defaultValue; } return shortObject.shortValue(); } /** * Gets an int from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The int is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as an int, defaultValue if null map input */ public static int getIntValue(final Map map, final K key, final int defaultValue) { final Integer integerObject = getInteger(map, key); if (integerObject == null) { return defaultValue; } return integerObject.intValue(); } /** * Gets a long from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The long is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a long, defaultValue if null map input */ public static long getLongValue(final Map map, final K key, final long defaultValue) { final Long longObject = getLong(map, key); if (longObject == null) { return defaultValue; } return longObject.longValue(); } /** * Gets a float from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The float is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a float, defaultValue if null map input */ public static float getFloatValue(final Map map, final K key, final float defaultValue) { final Float floatObject = getFloat(map, key); if (floatObject == null) { return defaultValue; } return floatObject.floatValue(); } /** * Gets a double from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The double is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a double, defaultValue if null map input */ public static double getDoubleValue(final Map map, final K key, final double defaultValue) { final Double doubleObject = getDouble(map, key); if (doubleObject == null) { return defaultValue; } return doubleObject.doubleValue(); } // Conversion methods //------------------------------------------------------------------------- /** * Gets a new Properties object initialised with the values from a Map. * A null input will return an empty properties object. * * @param the key type * @param the value type * @param map the map to convert to a Properties object * @return the properties object */ public static Properties toProperties(final Map map) { final Properties answer = new Properties(); if (map != null) { for (final Entry entry2 : map.entrySet()) { final Map.Entry entry = entry2; final Object key = entry.getKey(); final Object value = entry.getValue(); answer.put(key, value); } } return answer; } /** * Creates a new HashMap using data copied from a ResourceBundle. * * @param resourceBundle the resource bundle to convert, may not be null * @return the hashmap containing the data * @throws NullPointerException if the bundle is null */ public static Map toMap(final ResourceBundle resourceBundle) { final Enumeration enumeration = resourceBundle.getKeys(); final Map map = new HashMap(); while (enumeration.hasMoreElements()) { final String key = enumeration.nextElement(); final Object value = resourceBundle.getObject(key); map.put(key, value); } return map; } // Printing methods //------------------------------------------------------------------------- /** * Prints the given map with nice line breaks. *

* This method prints a nicely formatted String describing the Map. * Each map entry will be printed with key and value. * When the value is a Map, recursive behaviour occurs. *

* This method is NOT thread-safe in any special way. You must manually * synchronize on either this class or the stream as required. * * @param out the stream to print to, must not be null * @param label The label to be used, may be null. * If null, the label is not output. * It typically represents the name of the property in a bean or similar. * @param map The map to print, may be null. * If null, the text 'null' is output. * @throws NullPointerException if the stream is null */ @SuppressWarnings("deprecation") public static void verbosePrint(final PrintStream out, final Object label, final Map map) { verbosePrintInternal(out, label, map, new ArrayStack>(), false); } /** * Prints the given map with nice line breaks. *

* This method prints a nicely formatted String describing the Map. * Each map entry will be printed with key, value and value classname. * When the value is a Map, recursive behaviour occurs. *

* This method is NOT thread-safe in any special way. You must manually * synchronize on either this class or the stream as required. * * @param out the stream to print to, must not be null * @param label The label to be used, may be null. * If null, the label is not output. * It typically represents the name of the property in a bean or similar. * @param map The map to print, may be null. * If null, the text 'null' is output. * @throws NullPointerException if the stream is null */ @SuppressWarnings("deprecation") public static void debugPrint(final PrintStream out, final Object label, final Map map) { verbosePrintInternal(out, label, map, new ArrayStack>(), true); } // Implementation methods //------------------------------------------------------------------------- /** * Implementation providing functionality for {@link #debugPrint} and for * {@link #verbosePrint}. This prints the given map with nice line breaks. * If the debug flag is true, it additionally prints the type of the object * value. If the contents of a map include the map itself, then the text * (this Map) is printed out. If the contents include a * parent container of the map, the the text (ancestor[i] Map) is * printed, where i actually indicates the number of levels which must be * traversed in the sequential list of ancestors (e.g. father, grandfather, * great-grandfather, etc). * * @param out the stream to print to * @param label the label to be used, may be null. * If null, the label is not output. * It typically represents the name of the property in a bean or similar. * @param map the map to print, may be null. * If null, the text 'null' is output * @param lineage a stack consisting of any maps in which the previous * argument is contained. This is checked to avoid infinite recursion when * printing the output * @param debug flag indicating whether type names should be output. * @throws NullPointerException if the stream is null */ @SuppressWarnings("deprecation") private static void verbosePrintInternal(final PrintStream out, final Object label, final Map map, final ArrayStack> lineage, final boolean debug) { printIndent(out, lineage.size()); if (map == null) { if (label != null) { out.print(label); out.print(" = "); } out.println("null"); return; } if (label != null) { out.print(label); out.println(" = "); } printIndent(out, lineage.size()); out.println("{"); lineage.push(map); for (final Map.Entry entry : map.entrySet()) { final Object childKey = entry.getKey(); final Object childValue = entry.getValue(); if (childValue instanceof Map && !lineage.contains(childValue)) { verbosePrintInternal( out, childKey == null ? "null" : childKey, (Map) childValue, lineage, debug); } else { printIndent(out, lineage.size()); out.print(childKey); out.print(" = "); final int lineageIndex = lineage.indexOf(childValue); if (lineageIndex == -1) { out.print(childValue); } else if (lineage.size() - 1 == lineageIndex) { out.print("(this Map)"); } else { out.print( "(ancestor[" + (lineage.size() - 1 - lineageIndex - 1) + "] Map)"); } if (debug && childValue != null) { out.print(' '); out.println(childValue.getClass().getName()); } else { out.println(); } } } lineage.pop(); printIndent(out, lineage.size()); out.println(debug ? "} " + map.getClass().getName() : "}"); } /** * Writes indentation to the given stream. * * @param out the stream to indent */ private static void printIndent(final PrintStream out, final int indent) { for (int i = 0; i < indent; i++) { out.print(INDENT_STRING); } } // Misc //----------------------------------------------------------------------- /** * Inverts the supplied map returning a new HashMap such that the keys of * the input are swapped with the values. *

* This operation assumes that the inverse mapping is well defined. * If the input map had multiple entries with the same value mapped to * different keys, the returned map will map one of those keys to the * value, but the exact key which will be mapped is undefined. * * @param the key type * @param the value type * @param map the map to invert, may not be null * @return a new HashMap containing the inverted data * @throws NullPointerException if the map is null */ public static Map invertMap(final Map map) { final Map out = new HashMap(map.size()); for (final Entry entry : map.entrySet()) { out.put(entry.getValue(), entry.getKey()); } return out; } //----------------------------------------------------------------------- /** * Protects against adding null values to a map. *

* This method checks the value being added to the map, and if it is null * it is replaced by an empty string. *

* This could be useful if the map does not accept null values, or for * receiving data from a source that may provide null or empty string * which should be held in the same way in the map. *

* Keys are not validated. * Note that this method can be used to circumvent the map's * value type at runtime. * * @param the key type * @param map the map to add to, may not be null * @param key the key * @param value the value, null converted to "" * @throws NullPointerException if the map is null */ public static void safeAddToMap(final Map map, final K key, final Object value) throws NullPointerException { map.put(key, value == null ? "" : value); } //----------------------------------------------------------------------- /** * Puts all the keys and values from the specified array into the map. *

* This method is an alternative to the {@link java.util.Map#putAll(java.util.Map)} * method and constructors. It allows you to build a map from an object array * of various possible styles. *

* If the first entry in the object array implements {@link java.util.Map.Entry} * or {@link KeyValue} then the key and value are added from that object. * If the first entry in the object array is an object array itself, then * it is assumed that index 0 in the sub-array is the key and index 1 is the value. * Otherwise, the array is treated as keys and values in alternate indices. *

* For example, to create a color map: *

     * Map colorMap = MapUtils.putAll(new HashMap(), new String[][] {
     *     {"RED", "#FF0000"},
     *     {"GREEN", "#00FF00"},
     *     {"BLUE", "#0000FF"}
     * });
     * 
* or: *
     * Map colorMap = MapUtils.putAll(new HashMap(), new String[] {
     *     "RED", "#FF0000",
     *     "GREEN", "#00FF00",
     *     "BLUE", "#0000FF"
     * });
     * 
* or: *
     * Map colorMap = MapUtils.putAll(new HashMap(), new Map.Entry[] {
     *     new DefaultMapEntry("RED", "#FF0000"),
     *     new DefaultMapEntry("GREEN", "#00FF00"),
     *     new DefaultMapEntry("BLUE", "#0000FF")
     * });
     * 
* * @param the key type * @param the value type * @param map the map to populate, must not be null * @param array an array to populate from, null ignored * @return the input map * @throws NullPointerException if map is null * @throws IllegalArgumentException if sub-array or entry matching used and an entry is invalid * @throws ClassCastException if the array contents is mixed * @since 3.2 */ @SuppressWarnings("unchecked") // As per Javadoc throws CCE for invalid array contents public static Map putAll(final Map map, final Object[] array) { map.size(); // force NPE if (array == null || array.length == 0) { return map; } final Object obj = array[0]; if (obj instanceof Map.Entry) { for (final Object element : array) { // cast ok here, type is checked above final Map.Entry entry = (Map.Entry) element; map.put(entry.getKey(), entry.getValue()); } } else if (obj instanceof KeyValue) { for (final Object element : array) { // cast ok here, type is checked above final KeyValue keyval = (KeyValue) element; map.put(keyval.getKey(), keyval.getValue()); } } else if (obj instanceof Object[]) { for (int i = 0; i < array.length; i++) { final Object[] sub = (Object[]) array[i]; if (sub == null || sub.length < 2) { throw new IllegalArgumentException("Invalid array element: " + i); } // these casts can fail if array has incorrect types map.put((K) sub[0], (V) sub[1]); } } else { for (int i = 0; i < array.length - 1;) { // these casts can fail if array has incorrect types map.put((K) array[i++], (V) array[i++]); } } return map; } //----------------------------------------------------------------------- /** * Returns an immutable empty map if the argument is null, * or the argument itself otherwise. * * @param the key type * @param the value type * @param map the map, possibly null * @return an empty map if the argument is null */ public static Map emptyIfNull(final Map map) { return map == null ? Collections.emptyMap() : map; } /** * Null-safe check if the specified map is empty. *

* Null returns true. * * @param map the map to check, may be null * @return true if empty or null * @since 3.2 */ public static boolean isEmpty(final Map map) { return map == null || map.isEmpty(); } /** * Null-safe check if the specified map is not empty. *

* Null returns false. * * @param map the map to check, may be null * @return true if non-null and non-empty * @since 3.2 */ public static boolean isNotEmpty(final Map map) { return !MapUtils.isEmpty(map); } // Map decorators //----------------------------------------------------------------------- /** * Returns a synchronized map backed by the given map. *

* You must manually synchronize on the returned buffer's iterator to * avoid non-deterministic behavior: * *

     * Map m = MapUtils.synchronizedMap(myMap);
     * Set s = m.keySet();  // outside synchronized block
     * synchronized (m) {  // synchronized on MAP!
     *     Iterator i = s.iterator();
     *     while (i.hasNext()) {
     *         process (i.next());
     *     }
     * }
     * 
* * This method uses the implementation in {@link java.util.Collections Collections}. * * @param the key type * @param the value type * @param map the map to synchronize, must not be null * @return a synchronized map backed by the given map */ public static Map synchronizedMap(final Map map) { return Collections.synchronizedMap(map); } /** * Returns an unmodifiable map backed by the given map. *

* This method uses the implementation in the decorators subpackage. * * @param the key type * @param the value type * @param map the map to make unmodifiable, must not be null * @return an unmodifiable map backed by the given map * @throws IllegalArgumentException if the map is null */ public static Map unmodifiableMap(final Map map) { return UnmodifiableMap.unmodifiableMap(map); } /** * Returns a predicated (validating) map backed by the given map. *

* Only objects that pass the tests in the given predicates can be added to the map. * Trying to add an invalid object results in an IllegalArgumentException. * Keys must pass the key predicate, values must pass the value predicate. * It is important not to use the original map after invoking this method, * as it is a backdoor for adding invalid objects. * * @param the key type * @param the value type * @param map the map to predicate, must not be null * @param keyPred the predicate for keys, null means no check * @param valuePred the predicate for values, null means no check * @return a predicated map backed by the given map * @throws IllegalArgumentException if the Map is null */ public static IterableMap predicatedMap(final Map map, final Predicate keyPred, final Predicate valuePred) { return PredicatedMap.predicatedMap(map, keyPred, valuePred); } /** * Returns a transformed map backed by the given map. *

* This method returns a new map (decorating the specified map) that * will transform any new entries added to it. * Existing entries in the specified map will not be transformed. * If you want that behaviour, see {@link TransformedMap#transformedMap}. *

* Each object is passed through the transformers as it is added to the * Map. It is important not to use the original map after invoking this * method, as it is a backdoor for adding untransformed objects. *

* If there are any elements already in the map being decorated, they * are NOT transformed. * * @param the key type * @param the value type * @param map the map to transform, must not be null, typically empty * @param keyTransformer the transformer for the map keys, null means no transformation * @param valueTransformer the transformer for the map values, null means no transformation * @return a transformed map backed by the given map * @throws IllegalArgumentException if the Map is null */ public static IterableMap transformedMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { return TransformedMap.transformingMap(map, keyTransformer, valueTransformer); } /** * Returns a fixed-sized map backed by the given map. * Elements may not be added or removed from the returned map, but * existing elements can be changed (for instance, via the * {@link Map#put(Object,Object)} method). * * @param the key type * @param the value type * @param map the map whose size to fix, must not be null * @return a fixed-size map backed by that map * @throws IllegalArgumentException if the Map is null */ public static IterableMap fixedSizeMap(final Map map) { return FixedSizeMap.fixedSizeMap(map); } /** * Returns a "lazy" map whose values will be created on demand. *

* When the key passed to the returned map's {@link Map#get(Object)} * method is not present in the map, then the factory will be used * to create a new object and that object will become the value * associated with that key. *

* For instance: *

     * Factory factory = new Factory() {
     *     public Object create() {
     *         return new Date();
     *     }
     * }
     * Map lazyMap = MapUtils.lazyMap(new HashMap(), factory);
     * Object obj = lazyMap.get("test");
     * 
* * After the above code is executed, obj will contain * a new Date instance. Furthermore, that Date * instance is the value for the "test" key in the map. * * @param the key type * @param the value type * @param map the map to make lazy, must not be null * @param factory the factory for creating new objects, must not be null * @return a lazy map backed by the given map * @throws IllegalArgumentException if the Map or Factory is null */ public static IterableMap lazyMap(final Map map, final Factory factory) { return LazyMap.lazyMap(map, factory); } /** * Returns a "lazy" map whose values will be created on demand. *

* When the key passed to the returned map's {@link Map#get(Object)} * method is not present in the map, then the factory will be used * to create a new object and that object will become the value * associated with that key. The factory is a {@link Transformer} * that will be passed the key which it must transform into the value. *

* For instance: *

     * Transformer factory = new Transformer() {
     *     public Object transform(Object mapKey) {
     *         return new File(mapKey);
     *     }
     * }
     * Map lazyMap = MapUtils.lazyMap(new HashMap(), factory);
     * Object obj = lazyMap.get("C:/dev");
     * 
* * After the above code is executed, obj will contain * a new File instance for the C drive dev directory. * Furthermore, that File instance is the value for the * "C:/dev" key in the map. *

* If a lazy map is wrapped by a synchronized map, the result is a simple * synchronized cache. When an object is not is the cache, the cache itself * calls back to the factory Transformer to populate itself, all within the * same synchronized block. * * @param the key type * @param the value type * @param map the map to make lazy, must not be null * @param transformerFactory the factory for creating new objects, must not be null * @return a lazy map backed by the given map * @throws IllegalArgumentException if the Map or Transformer is null */ public static IterableMap lazyMap(final Map map, final Transformer transformerFactory) { return LazyMap.lazyMap(map, transformerFactory); } /** * Returns a map that maintains the order of keys that are added * backed by the given map. *

* If a key is added twice, the order is determined by the first add. * The order is observed through the keySet, values and entrySet. * * @param the key type * @param the value type * @param map the map to order, must not be null * @return an ordered map backed by the given map * @throws IllegalArgumentException if the Map is null */ public static OrderedMap orderedMap(final Map map) { return ListOrderedMap.listOrderedMap(map); } /** * Creates a mult-value map backed by the given map which returns * collections of type ArrayList. * * @param the key type * @param the value type * @param map the map to decorate * @return a multi-value map backed by the given map which returns ArrayLists of values. * @see MultiValueMap * @since 3.2 */ public static MultiValueMap multiValueMap(final Map> map) { return MultiValueMap.multiValueMap(map); } /** * Creates a multi-value map backed by the given map which returns * collections of the specified type. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionClass the type of collections to return from the map (must contain public no-arg constructor * and extend Collection). * @return a multi-value map backed by the given map which returns collections of the specified type * @see MultiValueMap * @since 3.2 */ public static > MultiValueMap multiValueMap(final Map map, final Class collectionClass) { return MultiValueMap.multiValueMap(map, collectionClass); } /** * Creates a multi-value map backed by the given map which returns * collections created by the specified collection factory. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionFactory a factor which creates collection objects * @return a multi-value map backed by the given map which returns collections * created by the specified collection factory * @see MultiValueMap * @since 3.2 */ public static > MultiValueMap multiValueMap(final Map map, final Factory collectionFactory) { return MultiValueMap.multiValueMap(map, collectionFactory); } // SortedMap decorators //----------------------------------------------------------------------- /** * Returns a synchronized sorted map backed by the given sorted map. *

* You must manually synchronize on the returned buffer's iterator to * avoid non-deterministic behavior: * *

     * Map m = MapUtils.synchronizedSortedMap(myMap);
     * Set s = m.keySet();  // outside synchronized block
     * synchronized (m) {  // synchronized on MAP!
     *     Iterator i = s.iterator();
     *     while (i.hasNext()) {
     *         process (i.next());
     *     }
     * }
     * 
* * This method uses the implementation in {@link java.util.Collections Collections}. * * @param the key type * @param the value type * @param map the map to synchronize, must not be null * @return a synchronized map backed by the given map * @throws IllegalArgumentException if the map is null */ public static SortedMap synchronizedSortedMap(final SortedMap map) { return Collections.synchronizedSortedMap(map); } /** * Returns an unmodifiable sorted map backed by the given sorted map. *

* This method uses the implementation in the decorators subpackage. * * @param the key type * @param the value type * @param map the sorted map to make unmodifiable, must not be null * @return an unmodifiable map backed by the given map * @throws IllegalArgumentException if the map is null */ public static SortedMap unmodifiableSortedMap(final SortedMap map) { return UnmodifiableSortedMap.unmodifiableSortedMap(map); } /** * Returns a predicated (validating) sorted map backed by the given map. *

* Only objects that pass the tests in the given predicates can be added to the map. * Trying to add an invalid object results in an IllegalArgumentException. * Keys must pass the key predicate, values must pass the value predicate. * It is important not to use the original map after invoking this method, * as it is a backdoor for adding invalid objects. * * @param the key type * @param the value type * @param map the map to predicate, must not be null * @param keyPred the predicate for keys, null means no check * @param valuePred the predicate for values, null means no check * @return a predicated map backed by the given map * @throws IllegalArgumentException if the SortedMap is null */ public static SortedMap predicatedSortedMap(final SortedMap map, final Predicate keyPred, final Predicate valuePred) { return PredicatedSortedMap.predicatedSortedMap(map, keyPred, valuePred); } /** * Returns a transformed sorted map backed by the given map. *

* This method returns a new sorted map (decorating the specified map) that * will transform any new entries added to it. * Existing entries in the specified map will not be transformed. * If you want that behaviour, see {@link TransformedSortedMap#transformedSortedMap}. *

* Each object is passed through the transformers as it is added to the * Map. It is important not to use the original map after invoking this * method, as it is a backdoor for adding untransformed objects. *

* If there are any elements already in the map being decorated, they * are NOT transformed. * * @param the key type * @param the value type * @param map the map to transform, must not be null, typically empty * @param keyTransformer the transformer for the map keys, null means no transformation * @param valueTransformer the transformer for the map values, null means no transformation * @return a transformed map backed by the given map * @throws IllegalArgumentException if the SortedMap is null */ public static SortedMap transformedSortedMap(final SortedMap map, final Transformer keyTransformer, final Transformer valueTransformer) { return TransformedSortedMap.transformingSortedMap(map, keyTransformer, valueTransformer); } /** * Returns a fixed-sized sorted map backed by the given sorted map. * Elements may not be added or removed from the returned map, but * existing elements can be changed (for instance, via the * {@link Map#put(Object,Object)} method). * * @param the key type * @param the value type * @param map the map whose size to fix, must not be null * @return a fixed-size map backed by that map * @throws IllegalArgumentException if the SortedMap is null */ public static SortedMap fixedSizeSortedMap(final SortedMap map) { return FixedSizeSortedMap.fixedSizeSortedMap(map); } /** * Returns a "lazy" sorted map whose values will be created on demand. *

* When the key passed to the returned map's {@link Map#get(Object)} * method is not present in the map, then the factory will be used * to create a new object and that object will become the value * associated with that key. *

* For instance: * *

     * Factory factory = new Factory() {
     *     public Object create() {
     *         return new Date();
     *     }
     * }
     * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
     * Object obj = lazy.get("test");
     * 
* * After the above code is executed, obj will contain * a new Date instance. Furthermore, that Date * instance is the value for the "test" key. * * @param the key type * @param the value type * @param map the map to make lazy, must not be null * @param factory the factory for creating new objects, must not be null * @return a lazy map backed by the given map * @throws IllegalArgumentException if the SortedMap or Factory is null */ public static SortedMap lazySortedMap(final SortedMap map, final Factory factory) { return LazySortedMap.lazySortedMap(map, factory); } /** * Returns a "lazy" sorted map whose values will be created on demand. *

* When the key passed to the returned map's {@link Map#get(Object)} * method is not present in the map, then the factory will be used * to create a new object and that object will become the value * associated with that key. The factory is a {@link Transformer} * that will be passed the key which it must transform into the value. *

* For instance: *

     * Transformer factory = new Transformer() {
     *     public Object transform(Object mapKey) {
     *         return new File(mapKey);
     *     }
     * }
     * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
     * Object obj = lazy.get("C:/dev");
     * 
* * After the above code is executed, obj will contain * a new File instance for the C drive dev directory. * Furthermore, that File instance is the value for the * "C:/dev" key in the map. *

* If a lazy map is wrapped by a synchronized map, the result is a simple * synchronized cache. When an object is not is the cache, the cache itself * calls back to the factory Transformer to populate itself, all within the * same synchronized block. * * @param the key type * @param the value type * @param map the map to make lazy, must not be null * @param transformerFactory the factory for creating new objects, must not be null * @return a lazy map backed by the given map * @throws IllegalArgumentException if the Map or Transformer is null */ public static SortedMap lazySortedMap(final SortedMap map, final Transformer transformerFactory) { return LazySortedMap.lazySortedMap(map, transformerFactory); } /** * Populates a Map using the supplied Transformer to transform the elements * into keys, using the unaltered element as the value in the Map. * * @param the key type * @param the value type * @param map the Map to populate. * @param elements the Iterable containing the input values for the map. * @param keyTransformer the Transformer used to transform the element into a key value * @throws NullPointerException if the map, elements or transformer are null */ public static void populateMap(final Map map, final Iterable elements, final Transformer keyTransformer) { populateMap(map, elements, keyTransformer, TransformerUtils.nopTransformer()); } /** * Populates a Map using the supplied Transformers to transform the elements * into keys and values. * * @param the key type * @param the value type * @param the type of object contained in the {@link Iterable} * @param map the Map to populate. * @param elements the Iterable containing the input values for the map. * @param keyTransformer the Transformer used to transform the element into a key value * @param valueTransformer the Transformer used to transform the element into a value * @throws NullPointerException if the map, elements or transformers are null */ public static void populateMap(final Map map, final Iterable elements, final Transformer keyTransformer, final Transformer valueTransformer) { final Iterator iter = elements.iterator(); while (iter.hasNext()) { final E temp = iter.next(); map.put(keyTransformer.transform(temp), valueTransformer.transform(temp)); } } /** * Populates a MultiMap using the supplied Transformer to transform the elements * into keys, using the unaltered element as the value in the MultiMap. * * @param the key type * @param the value type * @param map the MultiMap to populate. * @param elements the Iterable to use as input values for the map. * @param keyTransformer the Transformer used to transform the element into a key value * @throws NullPointerException if the map, elements or transformer are null */ public static void populateMap(final MultiMap map, final Iterable elements, final Transformer keyTransformer) { populateMap(map, elements, keyTransformer, TransformerUtils.nopTransformer()); } /** * Populates a MultiMap using the supplied Transformers to transform the elements * into keys and values. * * @param the key type * @param the value type * @param the type of object contained in the {@link Iterable} * @param map the MultiMap to populate. * @param elements the Iterable containing the input values for the map. * @param keyTransformer the Transformer used to transform the element into a key value * @param valueTransformer the Transformer used to transform the element into a value * @throws NullPointerException if the map, collection or transformers are null */ public static void populateMap(final MultiMap map, final Iterable elements, final Transformer keyTransformer, final Transformer valueTransformer) { final Iterator iter = elements.iterator(); while (iter.hasNext()) { final E temp = iter.next(); map.put(keyTransformer.transform(temp), valueTransformer.transform(temp)); } } /** * Get the specified {@link Map} as an {@link IterableMap}. * * @param the key type * @param the value type * @param map to wrap if necessary. * @return IterableMap * @since 4.0 */ public static IterableMap iterableMap(final Map map) { if (map == null) { throw new IllegalArgumentException("Map must not be null"); } return map instanceof IterableMap ? (IterableMap) map : new AbstractMapDecorator(map) {}; } /** * Get the specified {@link SortedMap} as an {@link IterableSortedMap}. * * @param the key type * @param the value type * @param sortedMap to wrap if necessary * @return {@link IterableSortedMap} * @since 4.0 */ public static IterableSortedMap iterableSortedMap(final SortedMap sortedMap) { if (sortedMap == null) { throw new IllegalArgumentException("Map must not be null"); } return sortedMap instanceof IterableSortedMap ? (IterableSortedMap) sortedMap : new AbstractSortedMapDecorator(sortedMap) {}; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 44402; to = 44315.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4; import java.io.PrintStream; import java.text.NumberFormat; import java.text.ParseException; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.ResourceBundle; import java.util.SortedMap; import java.util.TreeMap; import org.apache.commons.collections4.map.AbstractMapDecorator; import org.apache.commons.collections4.map.AbstractSortedMapDecorator; import org.apache.commons.collections4.map.FixedSizeMap; import org.apache.commons.collections4.map.FixedSizeSortedMap; import org.apache.commons.collections4.map.LazyMap; import org.apache.commons.collections4.map.LazySortedMap; import org.apache.commons.collections4.map.ListOrderedMap; import org.apache.commons.collections4.map.MultiValueMap; import org.apache.commons.collections4.map.PredicatedMap; import org.apache.commons.collections4.map.PredicatedSortedMap; import org.apache.commons.collections4.map.TransformedMap; import org.apache.commons.collections4.map.TransformedSortedMap; import org.apache.commons.collections4.map.UnmodifiableMap; import org.apache.commons.collections4.map.UnmodifiableSortedMap; /** * Provides utility methods and decorators for * {@link Map} and {@link SortedMap} instances. *

* It contains various type safe methods * as well as other useful features like deep copying. *

* It also provides the following decorators: * *

    *
  • {@link #fixedSizeMap(Map)} *
  • {@link #fixedSizeSortedMap(SortedMap)} *
  • {@link #lazyMap(Map,Factory)} *
  • {@link #lazyMap(Map,Transformer)} *
  • {@link #lazySortedMap(SortedMap,Factory)} *
  • {@link #lazySortedMap(SortedMap,Transformer)} *
  • {@link #predicatedMap(Map,Predicate,Predicate)} *
  • {@link #predicatedSortedMap(SortedMap,Predicate,Predicate)} *
  • {@link #transformedMap(Map, Transformer, Transformer)} *
  • {@link #transformedSortedMap(SortedMap, Transformer, Transformer)} *
  • {@link #multiValueMap( Map )} *
  • {@link #multiValueMap( Map, Class )} *
  • {@link #multiValueMap( Map, Factory )} *
* * @since 1.0 * @version $Id: MapUtils.java 1543964 2013-11-20 21:53:39Z tn $ */ public class MapUtils { /** * An empty unmodifiable sorted map. * This is not provided in the JDK. */ @SuppressWarnings("rawtypes") public static final SortedMap EMPTY_SORTED_MAP = UnmodifiableSortedMap.unmodifiableSortedMap(new TreeMap()); /** * String used to indent the verbose and debug Map prints. */ private static final String INDENT_STRING = " "; /** * MapUtils should not normally be instantiated. */ private MapUtils() {} // Type safe getters //------------------------------------------------------------------------- /** * Gets from a Map in a null-safe manner. * * @param the key type * @param the value type * @param map the map to use * @param key the key to look up * @return the value in the Map, null if null map input */ public static V getObject(final Map map, final K key) { if (map != null) { return map.get(key); } return null; } /** * Gets a String from a Map in a null-safe manner. *

* The String is obtained via toString. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a String, null if null map input */ public static String getString(final Map map, final K key) { if (map != null) { final Object answer = map.get(key); if (answer != null) { return answer.toString(); } } return null; } /** * Gets a Boolean from a Map in a null-safe manner. *

* If the value is a Boolean it is returned directly. * If the value is a String and it equals 'true' ignoring case * then true is returned, otherwise false. * If the value is a Number an integer zero value returns * false and non-zero returns true. * Otherwise, null is returned. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Boolean, null if null map input */ public static Boolean getBoolean(final Map map, final K key) { if (map != null) { final Object answer = map.get(key); if (answer != null) { if (answer instanceof Boolean) { return (Boolean) answer; } if (answer instanceof String) { return Boolean.valueOf((String) answer); } if (answer instanceof Number) { final Number n = (Number) answer; return n.intValue() != 0 ? Boolean.TRUE : Boolean.FALSE; } } } return null; } /** * Gets a Number from a Map in a null-safe manner. *

* If the value is a Number it is returned directly. * If the value is a String it is converted using * {@link NumberFormat#parse(String)} on the system default formatter * returning null if the conversion fails. * Otherwise, null is returned. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Number, null if null map input */ public static Number getNumber(final Map map, final K key) { if (map != null) { final Object answer = map.get(key); if (answer != null) { if (answer instanceof Number) { return (Number) answer; } if (answer instanceof String) { try { final String text = (String) answer; return NumberFormat.getInstance().parse(text); } catch (final ParseException e) { // NOPMD // failure means null is returned } } } } return null; } /** * Gets a Byte from a Map in a null-safe manner. *

* The Byte is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Byte, null if null map input */ public static Byte getByte(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Byte) { return (Byte) answer; } return Byte.valueOf(answer.byteValue()); } /** * Gets a Short from a Map in a null-safe manner. *

* The Short is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Short, null if null map input */ public static Short getShort(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Short) { return (Short) answer; } return Short.valueOf(answer.shortValue()); } /** * Gets a Integer from a Map in a null-safe manner. *

* The Integer is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Integer, null if null map input */ public static Integer getInteger(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Integer) { return (Integer) answer; } return Integer.valueOf(answer.intValue()); } /** * Gets a Long from a Map in a null-safe manner. *

* The Long is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Long, null if null map input */ public static Long getLong(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Long) { return (Long) answer; } return Long.valueOf(answer.longValue()); } /** * Gets a Float from a Map in a null-safe manner. *

* The Float is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Float, null if null map input */ public static Float getFloat(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Float) { return (Float) answer; } return Float.valueOf(answer.floatValue()); } /** * Gets a Double from a Map in a null-safe manner. *

* The Double is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Double, null if null map input */ public static Double getDouble(final Map map, final K key) { final Number answer = getNumber(map, key); if (answer == null) { return null; } if (answer instanceof Double) { return (Double) answer; } return Double.valueOf(answer.doubleValue()); } /** * Gets a Map from a Map in a null-safe manner. *

* If the value returned from the specified map is not a Map then * null is returned. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Map, null if null map input */ public static Map getMap(final Map map, final K key) { if (map != null) { final Object answer = map.get(key); if (answer != null && answer instanceof Map) { return (Map) answer; } } return null; } // Type safe getters with default values //------------------------------------------------------------------------- /** * Looks up the given key in the given map, converting null into the * given default value. * * @param the key type * @param the value type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null * @return the value in the map, or defaultValue if the original value * is null or the map is null */ public static V getObject(final Map map, final K key, final V defaultValue) { if (map != null) { final V answer = map.get(key); if (answer != null) { return answer; } } return defaultValue; } /** * Looks up the given key in the given map, converting the result into * a string, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a string, or defaultValue if the * original value is null, the map is null or the string conversion fails */ public static String getString(final Map map, final K key, final String defaultValue) { String answer = getString(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a boolean, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a boolean, or defaultValue if the * original value is null, the map is null or the boolean conversion fails */ public static Boolean getBoolean(final Map map, final K key, final Boolean defaultValue) { Boolean answer = getBoolean(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a number, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Number getNumber(final Map map, final K key, final Number defaultValue) { Number answer = getNumber(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a byte, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Byte getByte(final Map map, final K key, final Byte defaultValue) { Byte answer = getByte(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a short, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Short getShort(final Map map, final K key, final Short defaultValue) { Short answer = getShort(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * an integer, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Integer getInteger(final Map map, final K key, final Integer defaultValue) { Integer answer = getInteger(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a long, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Long getLong(final Map map, final K key, final Long defaultValue) { Long answer = getLong(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a float, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Float getFloat(final Map map, final K key, final Float defaultValue) { Float answer = getFloat(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a double, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the number conversion fails */ public static Double getDouble(final Map map, final K key, final Double defaultValue) { Double answer = getDouble(map, key); if (answer == null) { answer = defaultValue; } return answer; } /** * Looks up the given key in the given map, converting the result into * a map, using the default value if the the conversion fails. * * @param the key type * @param map the map whose value to look up * @param key the key of the value to look up in that map * @param defaultValue what to return if the value is null or if the * conversion fails * @return the value in the map as a number, or defaultValue if the * original value is null, the map is null or the map conversion fails */ public static Map getMap(final Map map, final K key, final Map defaultValue) { Map answer = getMap(map, key); if (answer == null) { answer = defaultValue; } return answer; } // Type safe primitive getters //------------------------------------------------------------------------- /** * Gets a boolean from a Map in a null-safe manner. *

* If the value is a Boolean its value is returned. * If the value is a String and it equals 'true' ignoring case * then true is returned, otherwise false. * If the value is a Number an integer zero value returns * false and non-zero returns true. * Otherwise, false is returned. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a Boolean, false if null map input */ public static boolean getBooleanValue(final Map map, final K key) { return Boolean.TRUE.equals(getBoolean(map, key)); } /** * Gets a byte from a Map in a null-safe manner. *

* The byte is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a byte, 0 if null map input */ public static byte getByteValue(final Map map, final K key) { final Byte byteObject = getByte(map, key); if (byteObject == null) { return 0; } return byteObject.byteValue(); } /** * Gets a short from a Map in a null-safe manner. *

* The short is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a short, 0 if null map input */ public static short getShortValue(final Map map, final K key) { final Short shortObject = getShort(map, key); if (shortObject == null) { return 0; } return shortObject.shortValue(); } /** * Gets an int from a Map in a null-safe manner. *

* The int is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as an int, 0 if null map input */ public static int getIntValue(final Map map, final K key) { final Integer integerObject = getInteger(map, key); if (integerObject == null) { return 0; } return integerObject.intValue(); } /** * Gets a long from a Map in a null-safe manner. *

* The long is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a long, 0L if null map input */ public static long getLongValue(final Map map, final K key) { final Long longObject = getLong(map, key); if (longObject == null) { return 0L; } return longObject.longValue(); } /** * Gets a float from a Map in a null-safe manner. *

* The float is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a float, 0.0F if null map input */ public static float getFloatValue(final Map map, final K key) { final Float floatObject = getFloat(map, key); if (floatObject == null) { return 0f; } return floatObject.floatValue(); } /** * Gets a double from a Map in a null-safe manner. *

* The double is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @return the value in the Map as a double, 0.0 if null map input */ public static double getDoubleValue(final Map map, final K key) { final Double doubleObject = getDouble(map, key); if (doubleObject == null) { return 0d; } return doubleObject.doubleValue(); } // Type safe primitive getters with default values //------------------------------------------------------------------------- /** * Gets a boolean from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* If the value is a Boolean its value is returned. * If the value is a String and it equals 'true' ignoring case * then true is returned, otherwise false. * If the value is a Number an integer zero value returns * false and non-zero returns true. * Otherwise, defaultValue is returned. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a Boolean, defaultValue if null map input */ public static boolean getBooleanValue(final Map map, final K key, final boolean defaultValue) { final Boolean booleanObject = getBoolean(map, key); if (booleanObject == null) { return defaultValue; } return booleanObject.booleanValue(); } /** * Gets a byte from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The byte is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a byte, defaultValue if null map input */ public static byte getByteValue(final Map map, final K key, final byte defaultValue) { final Byte byteObject = getByte(map, key); if (byteObject == null) { return defaultValue; } return byteObject.byteValue(); } /** * Gets a short from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The short is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a short, defaultValue if null map input */ public static short getShortValue(final Map map, final K key, final short defaultValue) { final Short shortObject = getShort(map, key); if (shortObject == null) { return defaultValue; } return shortObject.shortValue(); } /** * Gets an int from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The int is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as an int, defaultValue if null map input */ public static int getIntValue(final Map map, final K key, final int defaultValue) { final Integer integerObject = getInteger(map, key); if (integerObject == null) { return defaultValue; } return integerObject.intValue(); } /** * Gets a long from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The long is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a long, defaultValue if null map input */ public static long getLongValue(final Map map, final K key, final long defaultValue) { final Long longObject = getLong(map, key); if (longObject == null) { return defaultValue; } return longObject.longValue(); } /** * Gets a float from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The float is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a float, defaultValue if null map input */ public static float getFloatValue(final Map map, final K key, final float defaultValue) { final Float floatObject = getFloat(map, key); if (floatObject == null) { return defaultValue; } return floatObject.floatValue(); } /** * Gets a double from a Map in a null-safe manner, * using the default value if the the conversion fails. *

* The double is obtained from the results of {@link #getNumber(Map,Object)}. * * @param the key type * @param map the map to use * @param key the key to look up * @param defaultValue return if the value is null or if the conversion fails * @return the value in the Map as a double, defaultValue if null map input */ public static double getDoubleValue(final Map map, final K key, final double defaultValue) { final Double doubleObject = getDouble(map, key); if (doubleObject == null) { return defaultValue; } return doubleObject.doubleValue(); } // Conversion methods //------------------------------------------------------------------------- /** * Gets a new Properties object initialised with the values from a Map. * A null input will return an empty properties object. * * @param the key type * @param the value type * @param map the map to convert to a Properties object * @return the properties object */ public static Properties toProperties(final Map map) { final Properties answer = new Properties(); if (map != null) { for (final Entry entry2 : map.entrySet()) { final Map.Entry entry = entry2; final Object key = entry.getKey(); final Object value = entry.getValue(); answer.put(key, value); } } return answer; } /** * Creates a new HashMap using data copied from a ResourceBundle. * * @param resourceBundle the resource bundle to convert, may not be null * @return the hashmap containing the data * @throws NullPointerException if the bundle is null */ public static Map toMap(final ResourceBundle resourceBundle) { final Enumeration enumeration = resourceBundle.getKeys(); final Map map = new HashMap(); while (enumeration.hasMoreElements()) { final String key = enumeration.nextElement(); final Object value = resourceBundle.getObject(key); map.put(key, value); } return map; } // Printing methods //------------------------------------------------------------------------- /** * Prints the given map with nice line breaks. *

* This method prints a nicely formatted String describing the Map. * Each map entry will be printed with key and value. * When the value is a Map, recursive behaviour occurs. *

* This method is NOT thread-safe in any special way. You must manually * synchronize on either this class or the stream as required. * * @param out the stream to print to, must not be null * @param label The label to be used, may be null. * If null, the label is not output. * It typically represents the name of the property in a bean or similar. * @param map The map to print, may be null. * If null, the text 'null' is output. * @throws NullPointerException if the stream is null */ @SuppressWarnings("deprecation") public static void verbosePrint(final PrintStream out, final Object label, final Map map) { verbosePrintInternal(out, label, map, new ArrayStack>(), false); } /** * Prints the given map with nice line breaks. *

* This method prints a nicely formatted String describing the Map. * Each map entry will be printed with key, value and value classname. * When the value is a Map, recursive behaviour occurs. *

* This method is NOT thread-safe in any special way. You must manually * synchronize on either this class or the stream as required. * * @param out the stream to print to, must not be null * @param label The label to be used, may be null. * If null, the label is not output. * It typically represents the name of the property in a bean or similar. * @param map The map to print, may be null. * If null, the text 'null' is output. * @throws NullPointerException if the stream is null */ @SuppressWarnings("deprecation") public static void debugPrint(final PrintStream out, final Object label, final Map map) { verbosePrintInternal(out, label, map, new ArrayStack>(), true); } // Implementation methods //------------------------------------------------------------------------- /** * Implementation providing functionality for {@link #debugPrint} and for * {@link #verbosePrint}. This prints the given map with nice line breaks. * If the debug flag is true, it additionally prints the type of the object * value. If the contents of a map include the map itself, then the text * (this Map) is printed out. If the contents include a * parent container of the map, the the text (ancestor[i] Map) is * printed, where i actually indicates the number of levels which must be * traversed in the sequential list of ancestors (e.g. father, grandfather, * great-grandfather, etc). * * @param out the stream to print to * @param label the label to be used, may be null. * If null, the label is not output. * It typically represents the name of the property in a bean or similar. * @param map the map to print, may be null. * If null, the text 'null' is output * @param lineage a stack consisting of any maps in which the previous * argument is contained. This is checked to avoid infinite recursion when * printing the output * @param debug flag indicating whether type names should be output. * @throws NullPointerException if the stream is null */ @SuppressWarnings("deprecation") private static void verbosePrintInternal(final PrintStream out, final Object label, final Map map, final ArrayStack> lineage, final boolean debug) { printIndent(out, lineage.size()); if (map == null) { if (label != null) { out.print(label); out.print(" = "); } out.println("null"); return; } if (label != null) { out.print(label); out.println(" = "); } printIndent(out, lineage.size()); out.println("{"); lineage.push(map); for (final Map.Entry entry : map.entrySet()) { final Object childKey = entry.getKey(); final Object childValue = entry.getValue(); if (childValue instanceof Map && !lineage.contains(childValue)) { verbosePrintInternal( out, childKey == null ? "null" : childKey, (Map) childValue, lineage, debug); } else { printIndent(out, lineage.size()); out.print(childKey); out.print(" = "); final int lineageIndex = lineage.indexOf(childValue); if (lineageIndex == -1) { out.print(childValue); } else if (lineage.size() - 1 == lineageIndex) { out.print("(this Map)"); } else { out.print( "(ancestor[" + (lineage.size() - 1 - lineageIndex - 1) + "] Map)"); } if (debug && childValue != null) { out.print(' '); out.println(childValue.getClass().getName()); } else { out.println(); } } } lineage.pop(); printIndent(out, lineage.size()); out.println(debug ? "} " + map.getClass().getName() : "}"); } /** * Writes indentation to the given stream. * * @param out the stream to indent */ private static void printIndent(final PrintStream out, final int indent) { for (int i = 0; i < indent; i++) { out.print(INDENT_STRING); } } // Misc //----------------------------------------------------------------------- /** * Inverts the supplied map returning a new HashMap such that the keys of * the input are swapped with the values. *

* This operation assumes that the inverse mapping is well defined. * If the input map had multiple entries with the same value mapped to * different keys, the returned map will map one of those keys to the * value, but the exact key which will be mapped is undefined. * * @param the key type * @param the value type * @param map the map to invert, may not be null * @return a new HashMap containing the inverted data * @throws NullPointerException if the map is null */ public static Map invertMap(final Map map) { final Map out = new HashMap(map.size()); for (final Entry entry : map.entrySet()) { out.put(entry.getValue(), entry.getKey()); } return out; } //----------------------------------------------------------------------- /** * Protects against adding null values to a map. *

* This method checks the value being added to the map, and if it is null * it is replaced by an empty string. *

* This could be useful if the map does not accept null values, or for * receiving data from a source that may provide null or empty string * which should be held in the same way in the map. *

* Keys are not validated. * Note that this method can be used to circumvent the map's * value type at runtime. * * @param the key type * @param map the map to add to, may not be null * @param key the key * @param value the value, null converted to "" * @throws NullPointerException if the map is null */ public static void safeAddToMap(final Map map, final K key, final Object value) throws NullPointerException { map.put(key, value == null ? "" : value); } //----------------------------------------------------------------------- /** * Puts all the keys and values from the specified array into the map. *

* This method is an alternative to the {@link java.util.Map#putAll(java.util.Map)} * method and constructors. It allows you to build a map from an object array * of various possible styles. *

* If the first entry in the object array implements {@link java.util.Map.Entry} * or {@link KeyValue} then the key and value are added from that object. * If the first entry in the object array is an object array itself, then * it is assumed that index 0 in the sub-array is the key and index 1 is the value. * Otherwise, the array is treated as keys and values in alternate indices. *

* For example, to create a color map: *

     * Map colorMap = MapUtils.putAll(new HashMap(), new String[][] {
     *     {"RED", "#FF0000"},
     *     {"GREEN", "#00FF00"},
     *     {"BLUE", "#0000FF"}
     * });
     * 
* or: *
     * Map colorMap = MapUtils.putAll(new HashMap(), new String[] {
     *     "RED", "#FF0000",
     *     "GREEN", "#00FF00",
     *     "BLUE", "#0000FF"
     * });
     * 
* or: *
     * Map colorMap = MapUtils.putAll(new HashMap(), new Map.Entry[] {
     *     new DefaultMapEntry("RED", "#FF0000"),
     *     new DefaultMapEntry("GREEN", "#00FF00"),
     *     new DefaultMapEntry("BLUE", "#0000FF")
     * });
     * 
* * @param the key type * @param the value type * @param map the map to populate, must not be null * @param array an array to populate from, null ignored * @return the input map * @throws NullPointerException if map is null * @throws IllegalArgumentException if sub-array or entry matching used and an entry is invalid * @throws ClassCastException if the array contents is mixed * @since 3.2 */ @SuppressWarnings("unchecked") // As per Javadoc throws CCE for invalid array contents public static Map putAll(final Map map, final Object[] array) { map.size(); // force NPE if (array == null || array.length == 0) { return map; } final Object obj = array[0]; if (obj instanceof Map.Entry) { for (final Object element : array) { // cast ok here, type is checked above final Map.Entry entry = (Map.Entry) element; map.put(entry.getKey(), entry.getValue()); } } else if (obj instanceof KeyValue) { for (final Object element : array) { // cast ok here, type is checked above final KeyValue keyval = (KeyValue) element; map.put(keyval.getKey(), keyval.getValue()); } } else if (obj instanceof Object[]) { for (int i = 0; i < array.length; i++) { final Object[] sub = (Object[]) array[i]; if (sub == null || sub.length < 2) { throw new IllegalArgumentException("Invalid array element: " + i); } // these casts can fail if array has incorrect types map.put((K) sub[0], (V) sub[1]); } } else { for (int i = 0; i < array.length - 1;) { // these casts can fail if array has incorrect types map.put((K) array[i++], (V) array[i++]); } } return map; } //----------------------------------------------------------------------- /** * Returns an immutable empty map if the argument is null, * or the argument itself otherwise. * * @param the key type * @param the value type * @param map the map, possibly null * @return an empty map if the argument is null */ public static Map emptyIfNull(final Map map) { return map == null ? Collections.emptyMap() : map; } /** * Null-safe check if the specified map is empty. *

* Null returns true. * * @param map the map to check, may be null * @return true if empty or null * @since 3.2 */ public static boolean isEmpty(final Map map) { return map == null || map.isEmpty(); } /** * Null-safe check if the specified map is not empty. *

* Null returns false. * * @param map the map to check, may be null * @return true if non-null and non-empty * @since 3.2 */ public static boolean isNotEmpty(final Map map) { return !MapUtils.isEmpty(map); } // Map decorators //----------------------------------------------------------------------- /** * Returns a synchronized map backed by the given map. *

* You must manually synchronize on the returned buffer's iterator to * avoid non-deterministic behavior: * *

     * Map m = MapUtils.synchronizedMap(myMap);
     * Set s = m.keySet();  // outside synchronized block
     * synchronized (m) {  // synchronized on MAP!
     *     Iterator i = s.iterator();
     *     while (i.hasNext()) {
     *         process (i.next());
     *     }
     * }
     * 
* * This method uses the implementation in {@link java.util.Collections Collections}. * * @param the key type * @param the value type * @param map the map to synchronize, must not be null * @return a synchronized map backed by the given map */ public static Map synchronizedMap(final Map map) { return Collections.synchronizedMap(map); } /** * Returns an unmodifiable map backed by the given map. *

* This method uses the implementation in the decorators subpackage. * * @param the key type * @param the value type * @param map the map to make unmodifiable, must not be null * @return an unmodifiable map backed by the given map * @throws IllegalArgumentException if the map is null */ public static Map unmodifiableMap(final Map map) { return UnmodifiableMap.unmodifiableMap(map); } /** * Returns a predicated (validating) map backed by the given map. *

* Only objects that pass the tests in the given predicates can be added to the map. * Trying to add an invalid object results in an IllegalArgumentException. * Keys must pass the key predicate, values must pass the value predicate. * It is important not to use the original map after invoking this method, * as it is a backdoor for adding invalid objects. * * @param the key type * @param the value type * @param map the map to predicate, must not be null * @param keyPred the predicate for keys, null means no check * @param valuePred the predicate for values, null means no check * @return a predicated map backed by the given map * @throws IllegalArgumentException if the Map is null */ public static IterableMap predicatedMap(final Map map, final Predicate keyPred, final Predicate valuePred) { return PredicatedMap.predicatedMap(map, keyPred, valuePred); } /** * Returns a transformed map backed by the given map. *

* This method returns a new map (decorating the specified map) that * will transform any new entries added to it. * Existing entries in the specified map will not be transformed. * If you want that behaviour, see {@link TransformedMap#transformedMap}. *

* Each object is passed through the transformers as it is added to the * Map. It is important not to use the original map after invoking this * method, as it is a backdoor for adding untransformed objects. *

* If there are any elements already in the map being decorated, they * are NOT transformed. * * @param the key type * @param the value type * @param map the map to transform, must not be null, typically empty * @param keyTransformer the transformer for the map keys, null means no transformation * @param valueTransformer the transformer for the map values, null means no transformation * @return a transformed map backed by the given map * @throws IllegalArgumentException if the Map is null */ public static IterableMap transformedMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { return TransformedMap.transformingMap(map, keyTransformer, valueTransformer); } /** * Returns a fixed-sized map backed by the given map. * Elements may not be added or removed from the returned map, but * existing elements can be changed (for instance, via the * {@link Map#put(Object,Object)} method). * * @param the key type * @param the value type * @param map the map whose size to fix, must not be null * @return a fixed-size map backed by that map * @throws IllegalArgumentException if the Map is null */ public static IterableMap fixedSizeMap(final Map map) { return FixedSizeMap.fixedSizeMap(map); } /** * Returns a "lazy" map whose values will be created on demand. *

* When the key passed to the returned map's {@link Map#get(Object)} * method is not present in the map, then the factory will be used * to create a new object and that object will become the value * associated with that key. *

* For instance: *

     * Factory factory = new Factory() {
     *     public Object create() {
     *         return new Date();
     *     }
     * }
     * Map lazyMap = MapUtils.lazyMap(new HashMap(), factory);
     * Object obj = lazyMap.get("test");
     * 
* * After the above code is executed, obj will contain * a new Date instance. Furthermore, that Date * instance is the value for the "test" key in the map. * * @param the key type * @param the value type * @param map the map to make lazy, must not be null * @param factory the factory for creating new objects, must not be null * @return a lazy map backed by the given map * @throws IllegalArgumentException if the Map or Factory is null */ public static IterableMap lazyMap(final Map map, final Factory factory) { return LazyMap.lazyMap(map, factory); } /** * Returns a "lazy" map whose values will be created on demand. *

* When the key passed to the returned map's {@link Map#get(Object)} * method is not present in the map, then the factory will be used * to create a new object and that object will become the value * associated with that key. The factory is a {@link Transformer} * that will be passed the key which it must transform into the value. *

* For instance: *

     * Transformer factory = new Transformer() {
     *     public Object transform(Object mapKey) {
     *         return new File(mapKey);
     *     }
     * }
     * Map lazyMap = MapUtils.lazyMap(new HashMap(), factory);
     * Object obj = lazyMap.get("C:/dev");
     * 
* * After the above code is executed, obj will contain * a new File instance for the C drive dev directory. * Furthermore, that File instance is the value for the * "C:/dev" key in the map. *

* If a lazy map is wrapped by a synchronized map, the result is a simple * synchronized cache. When an object is not is the cache, the cache itself * calls back to the factory Transformer to populate itself, all within the * same synchronized block. * * @param the key type * @param the value type * @param map the map to make lazy, must not be null * @param transformerFactory the factory for creating new objects, must not be null * @return a lazy map backed by the given map * @throws IllegalArgumentException if the Map or Transformer is null */ public static IterableMap lazyMap(final Map map, final Transformer transformerFactory) { return LazyMap.lazyMap(map, transformerFactory); } /** * Returns a map that maintains the order of keys that are added * backed by the given map. *

* If a key is added twice, the order is determined by the first add. * The order is observed through the keySet, values and entrySet. * * @param the key type * @param the value type * @param map the map to order, must not be null * @return an ordered map backed by the given map * @throws IllegalArgumentException if the Map is null */ public static OrderedMap orderedMap(final Map map) { return ListOrderedMap.listOrderedMap(map); } /** * Creates a mult-value map backed by the given map which returns * collections of type ArrayList. * * @param the key type * @param the value type * @param map the map to decorate * @return a multi-value map backed by the given map which returns ArrayLists of values. * @see MultiValueMap * @since 3.2 */ public static MultiValueMap multiValueMap(final Map> map) { return MultiValueMap.multiValueMap(map); } /** * Creates a multi-value map backed by the given map which returns * collections of the specified type. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionClass the type of collections to return from the map (must contain public no-arg constructor * and extend Collection). * @return a multi-value map backed by the given map which returns collections of the specified type * @see MultiValueMap * @since 3.2 */ public static > MultiValueMap multiValueMap(final Map map, final Class collectionClass) { return MultiValueMap.multiValueMap(map, collectionClass); } /** * Creates a multi-value map backed by the given map which returns * collections created by the specified collection factory. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionFactory a factor which creates collection objects * @return a multi-value map backed by the given map which returns collections * created by the specified collection factory * @see MultiValueMap * @since 3.2 */ public static > MultiValueMap multiValueMap(final Map map, final Factory collectionFactory) { return MultiValueMap.multiValueMap(map, collectionFactory); } // SortedMap decorators //----------------------------------------------------------------------- /** * Returns a synchronized sorted map backed by the given sorted map. *

* You must manually synchronize on the returned buffer's iterator to * avoid non-deterministic behavior: * *

     * Map m = MapUtils.synchronizedSortedMap(myMap);
     * Set s = m.keySet();  // outside synchronized block
     * synchronized (m) {  // synchronized on MAP!
     *     Iterator i = s.iterator();
     *     while (i.hasNext()) {
     *         process (i.next());
     *     }
     * }
     * 
* * This method uses the implementation in {@link java.util.Collections Collections}. * * @param the key type * @param the value type * @param map the map to synchronize, must not be null * @return a synchronized map backed by the given map * @throws IllegalArgumentException if the map is null */ public static SortedMap synchronizedSortedMap(final SortedMap map) { return Collections.synchronizedSortedMap(map); } /** * Returns an unmodifiable sorted map backed by the given sorted map. *

* This method uses the implementation in the decorators subpackage. * * @param the key type * @param the value type * @param map the sorted map to make unmodifiable, must not be null * @return an unmodifiable map backed by the given map * @throws IllegalArgumentException if the map is null */ public static SortedMap unmodifiableSortedMap(final SortedMap map) { return UnmodifiableSortedMap.unmodifiableSortedMap(map); } /** * Returns a predicated (validating) sorted map backed by the given map. *

* Only objects that pass the tests in the given predicates can be added to the map. * Trying to add an invalid object results in an IllegalArgumentException. * Keys must pass the key predicate, values must pass the value predicate. * It is important not to use the original map after invoking this method, * as it is a backdoor for adding invalid objects. * * @param the key type * @param the value type * @param map the map to predicate, must not be null * @param keyPred the predicate for keys, null means no check * @param valuePred the predicate for values, null means no check * @return a predicated map backed by the given map * @throws IllegalArgumentException if the SortedMap is null */ public static SortedMap predicatedSortedMap(final SortedMap map, final Predicate keyPred, final Predicate valuePred) { return PredicatedSortedMap.predicatedSortedMap(map, keyPred, valuePred); } /** * Returns a transformed sorted map backed by the given map. *

* This method returns a new sorted map (decorating the specified map) that * will transform any new entries added to it. * Existing entries in the specified map will not be transformed. * If you want that behaviour, see {@link TransformedSortedMap#transformedSortedMap}. *

* Each object is passed through the transformers as it is added to the * Map. It is important not to use the original map after invoking this * method, as it is a backdoor for adding untransformed objects. *

* If there are any elements already in the map being decorated, they * are NOT transformed. * * @param the key type * @param the value type * @param map the map to transform, must not be null, typically empty * @param keyTransformer the transformer for the map keys, null means no transformation * @param valueTransformer the transformer for the map values, null means no transformation * @return a transformed map backed by the given map * @throws IllegalArgumentException if the SortedMap is null */ public static SortedMap transformedSortedMap(final SortedMap map, final Transformer keyTransformer, final Transformer valueTransformer) { return TransformedSortedMap.transformingSortedMap(map, keyTransformer, valueTransformer); } /** * Returns a fixed-sized sorted map backed by the given sorted map. * Elements may not be added or removed from the returned map, but * existing elements can be changed (for instance, via the * {@link Map#put(Object,Object)} method). * * @param the key type * @param the value type * @param map the map whose size to fix, must not be null * @return a fixed-size map backed by that map * @throws IllegalArgumentException if the SortedMap is null */ public static SortedMap fixedSizeSortedMap(final SortedMap map) { return FixedSizeSortedMap.fixedSizeSortedMap(map); } /** * Returns a "lazy" sorted map whose values will be created on demand. *

* When the key passed to the returned map's {@link Map#get(Object)} * method is not present in the map, then the factory will be used * to create a new object and that object will become the value * associated with that key. *

* For instance: * *

     * Factory factory = new Factory() {
     *     public Object create() {
     *         return new Date();
     *     }
     * }
     * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
     * Object obj = lazy.get("test");
     * 
* * After the above code is executed, obj will contain * a new Date instance. Furthermore, that Date * instance is the value for the "test" key. * * @param the key type * @param the value type * @param map the map to make lazy, must not be null * @param factory the factory for creating new objects, must not be null * @return a lazy map backed by the given map * @throws IllegalArgumentException if the SortedMap or Factory is null */ public static SortedMap lazySortedMap(final SortedMap map, final Factory factory) { return LazySortedMap.lazySortedMap(map, factory); } /** * Returns a "lazy" sorted map whose values will be created on demand. *

* When the key passed to the returned map's {@link Map#get(Object)} * method is not present in the map, then the factory will be used * to create a new object and that object will become the value * associated with that key. The factory is a {@link Transformer} * that will be passed the key which it must transform into the value. *

* For instance: *

     * Transformer factory = new Transformer() {
     *     public Object transform(Object mapKey) {
     *         return new File(mapKey);
     *     }
     * }
     * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
     * Object obj = lazy.get("C:/dev");
     * 
* * After the above code is executed, obj will contain * a new File instance for the C drive dev directory. * Furthermore, that File instance is the value for the * "C:/dev" key in the map. *

* If a lazy map is wrapped by a synchronized map, the result is a simple * synchronized cache. When an object is not is the cache, the cache itself * calls back to the factory Transformer to populate itself, all within the * same synchronized block. * * @param the key type * @param the value type * @param map the map to make lazy, must not be null * @param transformerFactory the factory for creating new objects, must not be null * @return a lazy map backed by the given map * @throws IllegalArgumentException if the Map or Transformer is null */ public static SortedMap lazySortedMap(final SortedMap map, final Transformer transformerFactory) { return LazySortedMap.lazySortedMap(map, transformerFactory); } /** * Populates a Map using the supplied Transformer to transform the elements * into keys, using the unaltered element as the value in the Map. * * @param the key type * @param the value type * @param map the Map to populate. * @param elements the Iterable containing the input values for the map. * @param keyTransformer the Transformer used to transform the element into a key value * @throws NullPointerException if the map, elements or transformer are null */ public static void populateMap(final Map map, final Iterable elements, final Transformer keyTransformer) { populateMap(map, elements, keyTransformer, TransformerUtils.nopTransformer()); } /** * Populates a Map using the supplied Transformers to transform the elements * into keys and values. * * @param the key type * @param the value type * @param the type of object contained in the {@link Iterable} * @param map the Map to populate. * @param elements the Iterable containing the input values for the map. * @param keyTransformer the Transformer used to transform the element into a key value * @param valueTransformer the Transformer used to transform the element into a value * @throws NullPointerException if the map, elements or transformers are null */ public static void populateMap(final Map map, final Iterable elements, final Transformer keyTransformer, final Transformer valueTransformer) { final Iterator iter = elements.iterator(); while (iter.hasNext()) { final E temp = iter.next(); map.put(keyTransformer.transform(temp), valueTransformer.transform(temp)); } } /** * Populates a MultiMap using the supplied Transformer to transform the elements * into keys, using the unaltered element as the value in the MultiMap. * * @param the key type * @param the value type * @param map the MultiMap to populate. * @param elements the Iterable to use as input values for the map. * @param keyTransformer the Transformer used to transform the element into a key value * @throws NullPointerException if the map, elements or transformer are null */ public static void populateMap(final MultiMap map, final Iterable elements, final Transformer keyTransformer) { populateMap(map, elements, keyTransformer, TransformerUtils.nopTransformer()); } /** * Populates a MultiMap using the supplied Transformers to transform the elements * into keys and values. * * @param the key type * @param the value type * @param the type of object contained in the {@link Iterable} * @param map the MultiMap to populate. * @param elements the Iterable containing the input values for the map. * @param keyTransformer the Transformer used to transform the element into a key value * @param valueTransformer the Transformer used to transform the element into a value * @throws NullPointerException if the map, collection or transformers are null */ public static void populateMap(final MultiMap map, final Iterable elements, final Transformer keyTransformer, final Transformer valueTransformer) { final Iterator iter = elements.iterator(); while (iter.hasNext()) { final E temp = iter.next(); map.put(keyTransformer.transform(temp), valueTransformer.transform(temp)); } } /** * Get the specified {@link Map} as an {@link IterableMap}. * * @param the key type * @param the value type * @param map to wrap if necessary. * @return IterableMap * @since 4.0 */ public static IterableMap iterableMap(final Map map) { if (map == null) { throw new IllegalArgumentException("Map must not be null"); } return map instanceof IterableMap ? (IterableMap) map : new AbstractMapDecorator(map) {}; } /** * Get the specified {@link SortedMap} as an {@link IterableSortedMap}. * * @param the key type * @param the value type * @param sortedMap to wrap if necessary * @return {@link IterableSortedMap} * @since 4.0 */ public static IterableSortedMap iterableSortedMap(final SortedMap sortedMap) { if (sortedMap == null) { throw new IllegalArgumentException("Map must not be null"); } return sortedMap instanceof IterableSortedMap ? (IterableSortedMap) sortedMap : new AbstractSortedMapDecorator(sortedMap) {}; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 44402; to = 44315.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.bidimap; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.set.UnmodifiableSet; import org.apache.commons.collections4.OrderedBidiMap; import org.apache.commons.collections4.OrderedMapIterator; import org.apache.commons.collections4.Unmodifiable; import org.apache.commons.collections4.iterators.UnmodifiableOrderedMapIterator; import org.apache.commons.collections4.map.UnmodifiableEntrySet; /** * Decorates another {@link OrderedBidiMap} to ensure it can't be altered. *

* Attempts to modify it will result in an UnsupportedOperationException. * * @since 3.0 * @version $Id: UnmodifiableOrderedBidiMap.java 1533984 2013-10-20 21:12:51Z tn $ */ public final class UnmodifiableOrderedBidiMap extends AbstractOrderedBidiMapDecorator implements Unmodifiable { /** The inverse unmodifiable map */ private UnmodifiableOrderedBidiMap inverse; /** * Factory method to create an unmodifiable map. *

* If the map passed in is already unmodifiable, it is returned. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @return an unmodifiable OrderedBidiMap * @throws IllegalArgumentException if map is null * @since 4.0 */ public static OrderedBidiMap unmodifiableOrderedBidiMap( final OrderedBidiMap map) { if (map instanceof Unmodifiable) { @SuppressWarnings("unchecked") // safe to upcast final OrderedBidiMap tmpMap = (OrderedBidiMap) map; return tmpMap; } return new UnmodifiableOrderedBidiMap(map); } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). * * @param map the map to decorate, must not be null * @throws IllegalArgumentException if map is null */ @SuppressWarnings("unchecked") // safe to upcast private UnmodifiableOrderedBidiMap(final OrderedBidiMap map) { super((OrderedBidiMap) map); } //----------------------------------------------------------------------- @Override public void clear() { throw new UnsupportedOperationException(); } @Override public V put(final K key, final V value) { throw new UnsupportedOperationException(); } @Override public void putAll(final Map mapToCopy) { throw new UnsupportedOperationException(); } @Override public V remove(final Object key) { throw new UnsupportedOperationException(); } @Override public Set> entrySet() { final Set> set = super.entrySet(); return UnmodifiableEntrySet.unmodifiableEntrySet(set); } @Override public Set keySet() { final Set set = super.keySet(); return UnmodifiableSet.unmodifiableSet(set); } @Override public Set values() { final Set set = super.values(); return UnmodifiableSet.unmodifiableSet(set); } //----------------------------------------------------------------------- @Override public K removeValue(final Object value) { throw new UnsupportedOperationException(); } @Override public OrderedBidiMap inverseBidiMap() { return inverseOrderedBidiMap(); } //----------------------------------------------------------------------- @Override public OrderedMapIterator mapIterator() { final OrderedMapIterator it = decorated().mapIterator(); return UnmodifiableOrderedMapIterator.unmodifiableOrderedMapIterator(it); } /** * Gets an unmodifiable view of this map where the keys and values are reversed. * * @return an inverted unmodifiable bidirectional map */ public OrderedBidiMap inverseOrderedBidiMap() { if (inverse == null) { inverse = new UnmodifiableOrderedBidiMap(decorated().inverseBidiMap()); inverse.inverse = this; } return inverse; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 2892; to = 2839.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.bidimap; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.set.UnmodifiableSet; import org.apache.commons.collections4.OrderedBidiMap; import org.apache.commons.collections4.OrderedMapIterator; import org.apache.commons.collections4.Unmodifiable; import org.apache.commons.collections4.iterators.UnmodifiableOrderedMapIterator; import org.apache.commons.collections4.map.UnmodifiableEntrySet; /** * Decorates another {@link OrderedBidiMap} to ensure it can't be altered. *

* Attempts to modify it will result in an UnsupportedOperationException. * * @since 3.0 * @version $Id: UnmodifiableOrderedBidiMap.java 1533984 2013-10-20 21:12:51Z tn $ */ public final class UnmodifiableOrderedBidiMap extends AbstractOrderedBidiMapDecorator implements Unmodifiable { /** The inverse unmodifiable map */ private UnmodifiableOrderedBidiMap inverse; /** * Factory method to create an unmodifiable map. *

* If the map passed in is already unmodifiable, it is returned. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @return an unmodifiable OrderedBidiMap * @throws IllegalArgumentException if map is null * @since 4.0 */ public static OrderedBidiMap unmodifiableOrderedBidiMap( final OrderedBidiMap map) { if (map instanceof Unmodifiable) { @SuppressWarnings("unchecked") // safe to upcast final OrderedBidiMap tmpMap = (OrderedBidiMap) map; return tmpMap; } return new UnmodifiableOrderedBidiMap(map); } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). * * @param map the map to decorate, must not be null * @throws IllegalArgumentException if map is null */ @SuppressWarnings("unchecked") // safe to upcast private UnmodifiableOrderedBidiMap(final OrderedBidiMap map) { super((OrderedBidiMap) map); } //----------------------------------------------------------------------- @Override public void clear() { throw new UnsupportedOperationException(); } @Override public V put(final K key, final V value) { throw new UnsupportedOperationException(); } @Override public void putAll(final Map mapToCopy) { throw new UnsupportedOperationException(); } @Override public V remove(final Object key) { throw new UnsupportedOperationException(); } @Override public Set> entrySet() { final Set> set = super.entrySet(); return UnmodifiableEntrySet.unmodifiableEntrySet(set); } @Override public Set keySet() { final Set set = super.keySet(); return UnmodifiableSet.unmodifiableSet(set); } @Override public Set values() { final Set set = super.values(); return UnmodifiableSet.unmodifiableSet(set); } //----------------------------------------------------------------------- @Override public K removeValue(final Object value) { throw new UnsupportedOperationException(); } @Override public OrderedBidiMap inverseBidiMap() { return inverseOrderedBidiMap(); } //----------------------------------------------------------------------- @Override public OrderedMapIterator mapIterator() { final OrderedMapIterator it = decorated().mapIterator(); return UnmodifiableOrderedMapIterator.unmodifiableOrderedMapIterator(it); } /** * Gets an unmodifiable view of this map where the keys and values are reversed. * * @return an inverted unmodifiable bidirectional map */ public OrderedBidiMap inverseOrderedBidiMap() { if (inverse == null) { inverse = new UnmodifiableOrderedBidiMap(decorated().inverseBidiMap()); inverse.inverse = this; } return inverse; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 2892; to = 2839.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.bidimap; import java.util.Map; import java.util.Set; import java.util.SortedMap; import org.apache.commons.collections4.set.UnmodifiableSet; import org.apache.commons.collections4.OrderedMapIterator; import org.apache.commons.collections4.SortedBidiMap; import org.apache.commons.collections4.Unmodifiable; import org.apache.commons.collections4.iterators.UnmodifiableOrderedMapIterator; import org.apache.commons.collections4.map.UnmodifiableEntrySet; import org.apache.commons.collections4.map.UnmodifiableSortedMap; /** * Decorates another {@link SortedBidiMap} to ensure it can't be altered. *

* Attempts to modify it will result in an {@link UnsupportedOperationException}. * * @since 3.0 * @version $Id: UnmodifiableSortedBidiMap.java 1533984 2013-10-20 21:12:51Z tn $ */ public final class UnmodifiableSortedBidiMap extends AbstractSortedBidiMapDecorator implements Unmodifiable { /** The inverse unmodifiable map */ private UnmodifiableSortedBidiMap inverse; /** * Factory method to create an unmodifiable map. *

* If the map passed in is already unmodifiable, it is returned. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @return an unmodifiable SortedBidiMap * @throws IllegalArgumentException if map is null * @since 4.0 */ public static SortedBidiMap unmodifiableSortedBidiMap(final SortedBidiMap map) { if (map instanceof Unmodifiable) { @SuppressWarnings("unchecked") // safe to upcast final SortedBidiMap tmpMap = (SortedBidiMap) map; return tmpMap; } return new UnmodifiableSortedBidiMap(map); } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). * * @param map the map to decorate, must not be null * @throws IllegalArgumentException if map is null */ @SuppressWarnings("unchecked") // safe to upcast private UnmodifiableSortedBidiMap(final SortedBidiMap map) { super((SortedBidiMap) map); } //----------------------------------------------------------------------- @Override public void clear() { throw new UnsupportedOperationException(); } @Override public V put(final K key, final V value) { throw new UnsupportedOperationException(); } @Override public void putAll(final Map mapToCopy) { throw new UnsupportedOperationException(); } @Override public V remove(final Object key) { throw new UnsupportedOperationException(); } @Override public Set> entrySet() { final Set> set = super.entrySet(); return UnmodifiableEntrySet.unmodifiableEntrySet(set); } @Override public Set keySet() { final Set set = super.keySet(); return UnmodifiableSet.unmodifiableSet(set); } @Override public Set values() { final Set set = super.values(); return UnmodifiableSet.unmodifiableSet(set); } //----------------------------------------------------------------------- @Override public K removeValue(final Object value) { throw new UnsupportedOperationException(); } //----------------------------------------------------------------------- @Override public OrderedMapIterator mapIterator() { final OrderedMapIterator it = decorated().mapIterator(); return UnmodifiableOrderedMapIterator.unmodifiableOrderedMapIterator(it); } //----------------------------------------------------------------------- @Override public SortedBidiMap inverseBidiMap() { if (inverse == null) { inverse = new UnmodifiableSortedBidiMap(decorated().inverseBidiMap()); inverse.inverse = this; } return inverse; } @Override public SortedMap subMap(final K fromKey, final K toKey) { final SortedMap sm = decorated().subMap(fromKey, toKey); return UnmodifiableSortedMap.unmodifiableSortedMap(sm); } @Override public SortedMap headMap(final K toKey) { final SortedMap sm = decorated().headMap(toKey); return UnmodifiableSortedMap.unmodifiableSortedMap(sm); } @Override public SortedMap tailMap(final K fromKey) { final SortedMap sm = decorated().tailMap(fromKey); return UnmodifiableSortedMap.unmodifiableSortedMap(sm); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 2958; to = 2905.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.bidimap; import java.util.Map; import java.util.Set; import java.util.SortedMap; import org.apache.commons.collections4.set.UnmodifiableSet; import org.apache.commons.collections4.OrderedMapIterator; import org.apache.commons.collections4.SortedBidiMap; import org.apache.commons.collections4.Unmodifiable; import org.apache.commons.collections4.iterators.UnmodifiableOrderedMapIterator; import org.apache.commons.collections4.map.UnmodifiableEntrySet; import org.apache.commons.collections4.map.UnmodifiableSortedMap; /** * Decorates another {@link SortedBidiMap} to ensure it can't be altered. *

* Attempts to modify it will result in an {@link UnsupportedOperationException}. * * @since 3.0 * @version $Id: UnmodifiableSortedBidiMap.java 1533984 2013-10-20 21:12:51Z tn $ */ public final class UnmodifiableSortedBidiMap extends AbstractSortedBidiMapDecorator implements Unmodifiable { /** The inverse unmodifiable map */ private UnmodifiableSortedBidiMap inverse; /** * Factory method to create an unmodifiable map. *

* If the map passed in is already unmodifiable, it is returned. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @return an unmodifiable SortedBidiMap * @throws IllegalArgumentException if map is null * @since 4.0 */ public static SortedBidiMap unmodifiableSortedBidiMap(final SortedBidiMap map) { if (map instanceof Unmodifiable) { @SuppressWarnings("unchecked") // safe to upcast final SortedBidiMap tmpMap = (SortedBidiMap) map; return tmpMap; } return new UnmodifiableSortedBidiMap(map); } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). * * @param map the map to decorate, must not be null * @throws IllegalArgumentException if map is null */ @SuppressWarnings("unchecked") // safe to upcast private UnmodifiableSortedBidiMap(final SortedBidiMap map) { super((SortedBidiMap) map); } //----------------------------------------------------------------------- @Override public void clear() { throw new UnsupportedOperationException(); } @Override public V put(final K key, final V value) { throw new UnsupportedOperationException(); } @Override public void putAll(final Map mapToCopy) { throw new UnsupportedOperationException(); } @Override public V remove(final Object key) { throw new UnsupportedOperationException(); } @Override public Set> entrySet() { final Set> set = super.entrySet(); return UnmodifiableEntrySet.unmodifiableEntrySet(set); } @Override public Set keySet() { final Set set = super.keySet(); return UnmodifiableSet.unmodifiableSet(set); } @Override public Set values() { final Set set = super.values(); return UnmodifiableSet.unmodifiableSet(set); } //----------------------------------------------------------------------- @Override public K removeValue(final Object value) { throw new UnsupportedOperationException(); } //----------------------------------------------------------------------- @Override public OrderedMapIterator mapIterator() { final OrderedMapIterator it = decorated().mapIterator(); return UnmodifiableOrderedMapIterator.unmodifiableOrderedMapIterator(it); } //----------------------------------------------------------------------- @Override public SortedBidiMap inverseBidiMap() { if (inverse == null) { inverse = new UnmodifiableSortedBidiMap(decorated().inverseBidiMap()); inverse.inverse = this; } return inverse; } @Override public SortedMap subMap(final K fromKey, final K toKey) { final SortedMap sm = decorated().subMap(fromKey, toKey); return UnmodifiableSortedMap.unmodifiableSortedMap(sm); } @Override public SortedMap headMap(final K toKey) { final SortedMap sm = decorated().headMap(toKey); return UnmodifiableSortedMap.unmodifiableSortedMap(sm); } @Override public SortedMap tailMap(final K fromKey) { final SortedMap sm = decorated().tailMap(fromKey); return UnmodifiableSortedMap.unmodifiableSortedMap(sm); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 2958; to = 2905.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.bidimap; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.set.UnmodifiableSet; import org.apache.commons.collections4.BidiMap; import org.apache.commons.collections4.MapIterator; import org.apache.commons.collections4.Unmodifiable; import org.apache.commons.collections4.iterators.UnmodifiableMapIterator; import org.apache.commons.collections4.map.UnmodifiableEntrySet; /** * Decorates another {@link BidiMap} to ensure it can't be altered. *

* Attempts to modify it will result in an UnsupportedOperationException. * * @since 3.0 * @version $Id: UnmodifiableBidiMap.java 1533984 2013-10-20 21:12:51Z tn $ */ public final class UnmodifiableBidiMap extends AbstractBidiMapDecorator implements Unmodifiable { /** The inverse unmodifiable map */ private UnmodifiableBidiMap inverse; /** * Factory method to create an unmodifiable map. *

* If the map passed in is already unmodifiable, it is returned. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @return an unmodifiable BidiMap * @throws IllegalArgumentException if map is null * @since 4.0 */ public static BidiMap unmodifiableBidiMap(final BidiMap map) { if (map instanceof Unmodifiable) { @SuppressWarnings("unchecked") // safe to upcast final BidiMap tmpMap = (BidiMap) map; return tmpMap; } return new UnmodifiableBidiMap(map); } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). * * @param map the map to decorate, must not be null * @throws IllegalArgumentException if map is null */ @SuppressWarnings("unchecked") // safe to upcast private UnmodifiableBidiMap(final BidiMap map) { super((BidiMap) map); } //----------------------------------------------------------------------- @Override public void clear() { throw new UnsupportedOperationException(); } @Override public V put(final K key, final V value) { throw new UnsupportedOperationException(); } @Override public void putAll(final Map mapToCopy) { throw new UnsupportedOperationException(); } @Override public V remove(final Object key) { throw new UnsupportedOperationException(); } @Override public Set> entrySet() { final Set> set = super.entrySet(); return UnmodifiableEntrySet.unmodifiableEntrySet(set); } @Override public Set keySet() { final Set set = super.keySet(); return UnmodifiableSet.unmodifiableSet(set); } @Override public Set values() { final Set set = super.values(); return UnmodifiableSet.unmodifiableSet(set); } //----------------------------------------------------------------------- @Override public K removeValue(final Object value) { throw new UnsupportedOperationException(); } @Override public MapIterator mapIterator() { final MapIterator it = decorated().mapIterator(); return UnmodifiableMapIterator.unmodifiableMapIterator(it); } @Override public synchronized BidiMap inverseBidiMap() { if (inverse == null) { inverse = new UnmodifiableBidiMap(decorated().inverseBidiMap()); inverse.inverse = this; } return inverse; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 2774; to = 2721.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.bidimap; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.set.UnmodifiableSet; import org.apache.commons.collections4.BidiMap; import org.apache.commons.collections4.MapIterator; import org.apache.commons.collections4.Unmodifiable; import org.apache.commons.collections4.iterators.UnmodifiableMapIterator; import org.apache.commons.collections4.map.UnmodifiableEntrySet; /** * Decorates another {@link BidiMap} to ensure it can't be altered. *

* Attempts to modify it will result in an UnsupportedOperationException. * * @since 3.0 * @version $Id: UnmodifiableBidiMap.java 1533984 2013-10-20 21:12:51Z tn $ */ public final class UnmodifiableBidiMap extends AbstractBidiMapDecorator implements Unmodifiable { /** The inverse unmodifiable map */ private UnmodifiableBidiMap inverse; /** * Factory method to create an unmodifiable map. *

* If the map passed in is already unmodifiable, it is returned. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @return an unmodifiable BidiMap * @throws IllegalArgumentException if map is null * @since 4.0 */ public static BidiMap unmodifiableBidiMap(final BidiMap map) { if (map instanceof Unmodifiable) { @SuppressWarnings("unchecked") // safe to upcast final BidiMap tmpMap = (BidiMap) map; return tmpMap; } return new UnmodifiableBidiMap(map); } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). * * @param map the map to decorate, must not be null * @throws IllegalArgumentException if map is null */ @SuppressWarnings("unchecked") // safe to upcast private UnmodifiableBidiMap(final BidiMap map) { super((BidiMap) map); } //----------------------------------------------------------------------- @Override public void clear() { throw new UnsupportedOperationException(); } @Override public V put(final K key, final V value) { throw new UnsupportedOperationException(); } @Override public void putAll(final Map mapToCopy) { throw new UnsupportedOperationException(); } @Override public V remove(final Object key) { throw new UnsupportedOperationException(); } @Override public Set> entrySet() { final Set> set = super.entrySet(); return UnmodifiableEntrySet.unmodifiableEntrySet(set); } @Override public Set keySet() { final Set set = super.keySet(); return UnmodifiableSet.unmodifiableSet(set); } @Override public Set values() { final Set set = super.values(); return UnmodifiableSet.unmodifiableSet(set); } //----------------------------------------------------------------------- @Override public K removeValue(final Object value) { throw new UnsupportedOperationException(); } @Override public MapIterator mapIterator() { final MapIterator it = decorated().mapIterator(); return UnmodifiableMapIterator.unmodifiableMapIterator(it); } @Override public synchronized BidiMap inverseBidiMap() { if (inverse == null) { inverse = new UnmodifiableBidiMap(decorated().inverseBidiMap()); inverse.inverse = this; } return inverse; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 2774; to = 2721.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.bidimap; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractSet; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import org.apache.commons.collections4.KeyValue; import org.apache.commons.collections4.MapIterator; import org.apache.commons.collections4.OrderedBidiMap; import org.apache.commons.collections4.OrderedIterator; import org.apache.commons.collections4.OrderedMapIterator; import org.apache.commons.collections4.iterators.EmptyOrderedMapIterator; import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry; import static org.apache.commons.collections4.bidimap.TreeBidiMap.DataElement.KEY; import static org.apache.commons.collections4.bidimap.TreeBidiMap.DataElement.VALUE; /** * Red-Black tree-based implementation of BidiMap where all objects added * implement the Comparable interface. *

* This class guarantees that the map will be in both ascending key order * and ascending value order, sorted according to the natural order for * the key's and value's classes. *

* This Map is intended for applications that need to be able to look * up a key-value pairing by either key or value, and need to do so * with equal efficiency. *

* While that goal could be accomplished by taking a pair of TreeMaps * and redirecting requests to the appropriate TreeMap (e.g., * containsKey would be directed to the TreeMap that maps values to * keys, containsValue would be directed to the TreeMap that maps keys * to values), there are problems with that implementation. * If the data contained in the TreeMaps is large, the cost of redundant * storage becomes significant. The {@link DualTreeBidiMap} and * {@link DualHashBidiMap} implementations use this approach. *

* This solution keeps minimizes the data storage by holding data only once. * The red-black algorithm is based on {@link java.util.TreeMap}, but has been modified * to simultaneously map a tree node by key and by value. This doubles the * cost of put operations (but so does using two TreeMaps), and nearly doubles * the cost of remove operations (there is a savings in that the lookup of the * node to be removed only has to be performed once). And since only one node * contains the key and value, storage is significantly less than that * required by two TreeMaps. *

* The Map.Entry instances returned by the appropriate methods will * not allow setValue() and will throw an * UnsupportedOperationException on attempts to call that method. * * @since 3.0 (previously DoubleOrderedMap v2.0) * @version $Id: TreeBidiMap.java 1543253 2013-11-19 00:43:25Z ggregory $ */ public class TreeBidiMap, V extends Comparable> implements OrderedBidiMap, Serializable { static enum DataElement { KEY("key"), VALUE("value"); private final String description; /** * Create a new TreeBidiMap.DataElement. * * @param description the description for the element */ private DataElement(final String description) { this.description = description; } @Override public String toString() { return description; } } private static final long serialVersionUID = 721969328361807L; private transient Node[] rootNode; private transient int nodeCount = 0; private transient int modifications = 0; private transient Set keySet; private transient Set valuesSet; private transient Set> entrySet; private transient Inverse inverse = null; //----------------------------------------------------------------------- /** * Constructs a new empty TreeBidiMap. */ @SuppressWarnings("unchecked") public TreeBidiMap() { super(); rootNode = new Node[2]; } /** * Constructs a new TreeBidiMap by copying an existing Map. * * @param map the map to copy * @throws ClassCastException if the keys/values in the map are * not Comparable or are not mutually comparable * @throws NullPointerException if any key or value in the map is null */ public TreeBidiMap(final Map map) { this(); putAll(map); } //----------------------------------------------------------------------- /** * Returns the number of key-value mappings in this map. * * @return the number of key-value mappings in this map */ public int size() { return nodeCount; } /** * Checks whether the map is empty or not. * * @return true if the map is empty */ public boolean isEmpty() { return nodeCount == 0; } /** * Checks whether this map contains the a mapping for the specified key. *

* The key must implement Comparable. * * @param key key whose presence in this map is to be tested * @return true if this map contains a mapping for the specified key * @throws ClassCastException if the key is of an inappropriate type * @throws NullPointerException if the key is null */ public boolean containsKey(final Object key) { checkKey(key); return lookupKey(key) != null; } /** * Checks whether this map contains the a mapping for the specified value. *

* The value must implement Comparable. * * @param value value whose presence in this map is to be tested * @return true if this map contains a mapping for the specified value * @throws ClassCastException if the value is of an inappropriate type * @throws NullPointerException if the value is null */ public boolean containsValue(final Object value) { checkValue(value); return lookupValue(value) != null; } /** * Gets the value to which this map maps the specified key. * Returns null if the map contains no mapping for this key. *

* The key must implement Comparable. * * @param key key whose associated value is to be returned * @return the value to which this map maps the specified key, * or null if the map contains no mapping for this key * @throws ClassCastException if the key is of an inappropriate type * @throws NullPointerException if the key is null */ public V get(final Object key) { checkKey(key); final Node node = lookupKey(key); return node == null ? null : node.getValue(); } /** * Puts the key-value pair into the map, replacing any previous pair. *

* When adding a key-value pair, the value may already exist in the map * against a different key. That mapping is removed, to ensure that the * value only occurs once in the inverse map. *

     *  BidiMap map1 = new TreeBidiMap();
     *  map.put("A","B");  // contains A mapped to B, as per Map
     *  map.put("A","C");  // contains A mapped to C, as per Map
     *
     *  BidiMap map2 = new TreeBidiMap();
     *  map.put("A","B");  // contains A mapped to B, as per Map
     *  map.put("C","B");  // contains C mapped to B, key A is removed
     * 
*

* Both key and value must implement Comparable. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value for the key * @throws ClassCastException if the key is of an inappropriate type * @throws NullPointerException if the key is null */ public V put(final K key, final V value) { final V result = get(key); doPut(key, value); return result; } /** * Puts all the mappings from the specified map into this map. *

* All keys and values must implement Comparable. * * @param map the map to copy from */ public void putAll(final Map map) { for (final Map.Entry e : map.entrySet()) { put(e.getKey(), e.getValue()); } } /** * Removes the mapping for this key from this map if present. *

* The key must implement Comparable. * * @param key key whose mapping is to be removed from the map. * @return previous value associated with specified key, * or null if there was no mapping for key. * @throws ClassCastException if the key is of an inappropriate type * @throws NullPointerException if the key is null */ public V remove(final Object key) { return doRemoveKey(key); } /** * Removes all mappings from this map. */ public void clear() { modify(); nodeCount = 0; rootNode[KEY.ordinal()] = null; rootNode[VALUE.ordinal()] = null; } //----------------------------------------------------------------------- /** * Returns the key to which this map maps the specified value. * Returns null if the map contains no mapping for this value. *

* The value must implement Comparable. * * @param value value whose associated key is to be returned. * @return the key to which this map maps the specified value, * or null if the map contains no mapping for this value. * @throws ClassCastException if the value is of an inappropriate type * @throws NullPointerException if the value is null */ public K getKey(final Object value) { checkValue(value); final Node node = lookupValue(value); return node == null ? null : node.getKey(); } /** * Removes the mapping for this value from this map if present. *

* The value must implement Comparable. * * @param value value whose mapping is to be removed from the map * @return previous key associated with specified value, * or null if there was no mapping for value. * @throws ClassCastException if the value is of an inappropriate type * @throws NullPointerException if the value is null */ public K removeValue(final Object value) { return doRemoveValue(value); } //----------------------------------------------------------------------- /** * Gets the first (lowest) key currently in this map. * * @return the first (lowest) key currently in this sorted map * @throws NoSuchElementException if this map is empty */ public K firstKey() { if (nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } return leastNode(rootNode[KEY.ordinal()], KEY).getKey(); } /** * Gets the last (highest) key currently in this map. * * @return the last (highest) key currently in this sorted map * @throws NoSuchElementException if this map is empty */ public K lastKey() { if (nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } return greatestNode(rootNode[KEY.ordinal()], KEY).getKey(); } /** * Gets the next key after the one specified. *

* The key must implement Comparable. * * @param key the key to search for next from * @return the next key, null if no match or at end */ public K nextKey(final K key) { checkKey(key); final Node node = nextGreater(lookupKey(key), KEY); return node == null ? null : node.getKey(); } /** * Gets the previous key before the one specified. *

* The key must implement Comparable. * * @param key the key to search for previous from * @return the previous key, null if no match or at start */ public K previousKey(final K key) { checkKey(key); final Node node = nextSmaller(lookupKey(key), KEY); return node == null ? null : node.getKey(); } //----------------------------------------------------------------------- /** * Returns a set view of the keys contained in this map in key order. *

* The set is backed by the map, so changes to the map are reflected in * the set, and vice-versa. If the map is modified while an iteration over * the set is in progress, the results of the iteration are undefined. *

* The set supports element removal, which removes the corresponding mapping * from the map. It does not support the add or addAll operations. * * @return a set view of the keys contained in this map. */ public Set keySet() { if (keySet == null) { keySet = new KeyView(KEY); } return keySet; } //----------------------------------------------------------------------- /** * Returns a set view of the values contained in this map in key order. * The returned object can be cast to a Set. *

* The set is backed by the map, so changes to the map are reflected in * the set, and vice-versa. If the map is modified while an iteration over * the set is in progress, the results of the iteration are undefined. *

* The set supports element removal, which removes the corresponding mapping * from the map. It does not support the add or addAll operations. * * @return a set view of the values contained in this map. */ public Set values() { if (valuesSet == null) { valuesSet = new ValueView(KEY); } return valuesSet; } //----------------------------------------------------------------------- /** * Returns a set view of the entries contained in this map in key order. * For simple iteration through the map, the MapIterator is quicker. *

* The set is backed by the map, so changes to the map are reflected in * the set, and vice-versa. If the map is modified while an iteration over * the set is in progress, the results of the iteration are undefined. *

* The set supports element removal, which removes the corresponding mapping * from the map. It does not support the add or addAll operations. * The returned MapEntry objects do not support setValue. * * @return a set view of the values contained in this map. */ public Set> entrySet() { if (entrySet == null) { entrySet = new EntryView(); } return entrySet; } //----------------------------------------------------------------------- public OrderedMapIterator mapIterator() { if (isEmpty()) { return EmptyOrderedMapIterator.emptyOrderedMapIterator(); } return new ViewMapIterator(KEY); } //----------------------------------------------------------------------- /** * Gets the inverse map for comparison. * * @return the inverse map */ public OrderedBidiMap inverseBidiMap() { if (inverse == null) { inverse = new Inverse(); } return inverse; } //----------------------------------------------------------------------- /** * Compares for equals as per the API. * * @param obj the object to compare to * @return true if equal */ @Override public boolean equals(final Object obj) { return this.doEquals(obj, KEY); } /** * Gets the hash code value for this map as per the API. * * @return the hash code value for this map */ @Override public int hashCode() { return this.doHashCode(KEY); } /** * Returns a string version of this Map in standard format. * * @return a standard format string version of the map */ @Override public String toString() { return this.doToString(KEY); } //----------------------------------------------------------------------- /** * Put logic. * * @param key the key, always the main map key * @param value the value, always the main map value */ private void doPut(final K key, final V value) { checkKeyAndValue(key, value); // store previous and remove previous mappings doRemoveKey(key); doRemoveValue(value); Node node = rootNode[KEY.ordinal()]; if (node == null) { // map is empty final Node root = new Node(key, value); rootNode[KEY.ordinal()] = root; rootNode[VALUE.ordinal()] = root; grow(); } else { // add new mapping while (true) { final int cmp = compare(key, node.getKey()); if (cmp == 0) { // shouldn't happen throw new IllegalArgumentException("Cannot store a duplicate key (\"" + key + "\") in this Map"); } else if (cmp < 0) { if (node.getLeft(KEY) != null) { node = node.getLeft(KEY); } else { final Node newNode = new Node(key, value); insertValue(newNode); node.setLeft(newNode, KEY); newNode.setParent(node, KEY); doRedBlackInsert(newNode, KEY); grow(); break; } } else { // cmp > 0 if (node.getRight(KEY) != null) { node = node.getRight(KEY); } else { final Node newNode = new Node(key, value); insertValue(newNode); node.setRight(newNode, KEY); newNode.setParent(node, KEY); doRedBlackInsert(newNode, KEY); grow(); break; } } } } } private V doRemoveKey(final Object key) { final Node node = lookupKey(key); if (node == null) { return null; } doRedBlackDelete(node); return node.getValue(); } private K doRemoveValue(final Object value) { final Node node = lookupValue(value); if (node == null) { return null; } doRedBlackDelete(node); return node.getKey(); } /** * do the actual lookup of a piece of data * * @param data the key or value to be looked up * @param index the KEY or VALUE int * @return the desired Node, or null if there is no mapping of the * specified data */ @SuppressWarnings("unchecked") private > Node lookup(final Object data, final DataElement dataElement) { Node rval = null; Node node = rootNode[dataElement.ordinal()]; while (node != null) { final int cmp = compare((T) data, (T) node.getData(dataElement)); if (cmp == 0) { rval = node; break; } else { node = cmp < 0 ? node.getLeft(dataElement) : node.getRight(dataElement); } } return rval; } private Node lookupKey(final Object key) { return this.lookup(key, KEY); } private Node lookupValue(final Object value) { return this.lookup(value, VALUE); } /** * get the next larger node from the specified node * * @param node the node to be searched from * @param index the KEY or VALUE int * @return the specified node */ private Node nextGreater(final Node node, final DataElement dataElement) { Node rval; if (node == null) { rval = null; } else if (node.getRight(dataElement) != null) { // everything to the node's right is larger. The least of // the right node's descendants is the next larger node rval = leastNode(node.getRight(dataElement), dataElement); } else { // traverse up our ancestry until we find an ancestor that // is null or one whose left child is our ancestor. If we // find a null, then this node IS the largest node in the // tree, and there is no greater node. Otherwise, we are // the largest node in the subtree on that ancestor's left // ... and that ancestor is the next greatest node Node parent = node.getParent(dataElement); Node child = node; while (parent != null && child == parent.getRight(dataElement)) { child = parent; parent = parent.getParent(dataElement); } rval = parent; } return rval; } /** * get the next larger node from the specified node * * @param node the node to be searched from * @param index the KEY or VALUE int * @return the specified node */ private Node nextSmaller(final Node node, final DataElement dataElement) { Node rval; if (node == null) { rval = null; } else if (node.getLeft(dataElement) != null) { // everything to the node's left is smaller. The greatest of // the left node's descendants is the next smaller node rval = greatestNode(node.getLeft(dataElement), dataElement); } else { // traverse up our ancestry until we find an ancestor that // is null or one whose right child is our ancestor. If we // find a null, then this node IS the largest node in the // tree, and there is no greater node. Otherwise, we are // the largest node in the subtree on that ancestor's right // ... and that ancestor is the next greatest node Node parent = node.getParent(dataElement); Node child = node; while (parent != null && child == parent.getLeft(dataElement)) { child = parent; parent = parent.getParent(dataElement); } rval = parent; } return rval; } //----------------------------------------------------------------------- /** * Compare two objects * * @param o1 the first object * @param o2 the second object * * @return negative value if o1 < o2; 0 if o1 == o2; positive * value if o1 > o2 */ private static > int compare(final T o1, final T o2) { return o1.compareTo(o2); } /** * Find the least node from a given node. * * @param node the node from which we will start searching * @param index the KEY or VALUE int * @return the smallest node, from the specified node, in the * specified mapping */ private Node leastNode(final Node node, final DataElement dataElement) { Node rval = node; if (rval != null) { while (rval.getLeft(dataElement) != null) { rval = rval.getLeft(dataElement); } } return rval; } /** * Find the greatest node from a given node. * * @param node the node from which we will start searching * @param index the KEY or VALUE int * @return the greatest node, from the specified node */ private Node greatestNode(final Node node, final DataElement dataElement) { Node rval = node; if (rval != null) { while (rval.getRight(dataElement) != null) { rval = rval.getRight(dataElement); } } return rval; } /** * copy the color from one node to another, dealing with the fact * that one or both nodes may, in fact, be null * * @param from the node whose color we're copying; may be null * @param to the node whose color we're changing; may be null * @param index the KEY or VALUE int */ private void copyColor(final Node from, final Node to, final DataElement dataElement) { if (to != null) { if (from == null) { // by default, make it black to.setBlack(dataElement); } else { to.copyColor(from, dataElement); } } } /** * is the specified node red? if the node does not exist, no, it's * black, thank you * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private static boolean isRed(final Node node, final DataElement dataElement) { return node != null && node.isRed(dataElement); } /** * is the specified black red? if the node does not exist, sure, * it's black, thank you * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private static boolean isBlack(final Node node, final DataElement dataElement) { return node == null || node.isBlack(dataElement); } /** * force a node (if it exists) red * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private static void makeRed(final Node node, final DataElement dataElement) { if (node != null) { node.setRed(dataElement); } } /** * force a node (if it exists) black * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private static void makeBlack(final Node node, final DataElement dataElement) { if (node != null) { node.setBlack(dataElement); } } /** * get a node's grandparent. mind you, the node, its parent, or * its grandparent may not exist. no problem * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private Node getGrandParent(final Node node, final DataElement dataElement) { return getParent(getParent(node, dataElement), dataElement); } /** * get a node's parent. mind you, the node, or its parent, may not * exist. no problem * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private Node getParent(final Node node, final DataElement dataElement) { return node == null ? null : node.getParent(dataElement); } /** * get a node's right child. mind you, the node may not exist. no * problem * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private Node getRightChild(final Node node, final DataElement dataElement) { return node == null ? null : node.getRight(dataElement); } /** * get a node's left child. mind you, the node may not exist. no * problem * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private Node getLeftChild(final Node node, final DataElement dataElement) { return node == null ? null : node.getLeft(dataElement); } /** * do a rotate left. standard fare in the world of balanced trees * * @param node the node to be rotated * @param index the KEY or VALUE int */ private void rotateLeft(final Node node, final DataElement dataElement) { final Node rightChild = node.getRight(dataElement); node.setRight(rightChild.getLeft(dataElement), dataElement); if (rightChild.getLeft(dataElement) != null) { rightChild.getLeft(dataElement).setParent(node, dataElement); } rightChild.setParent(node.getParent(dataElement), dataElement); if (node.getParent(dataElement) == null) { // node was the root ... now its right child is the root rootNode[dataElement.ordinal()] = rightChild; } else if (node.getParent(dataElement).getLeft(dataElement) == node) { node.getParent(dataElement).setLeft(rightChild, dataElement); } else { node.getParent(dataElement).setRight(rightChild, dataElement); } rightChild.setLeft(node, dataElement); node.setParent(rightChild, dataElement); } /** * do a rotate right. standard fare in the world of balanced trees * * @param node the node to be rotated * @param index the KEY or VALUE int */ private void rotateRight(final Node node, final DataElement dataElement) { final Node leftChild = node.getLeft(dataElement); node.setLeft(leftChild.getRight(dataElement), dataElement); if (leftChild.getRight(dataElement) != null) { leftChild.getRight(dataElement).setParent(node, dataElement); } leftChild.setParent(node.getParent(dataElement), dataElement); if (node.getParent(dataElement) == null) { // node was the root ... now its left child is the root rootNode[dataElement.ordinal()] = leftChild; } else if (node.getParent(dataElement).getRight(dataElement) == node) { node.getParent(dataElement).setRight(leftChild, dataElement); } else { node.getParent(dataElement).setLeft(leftChild, dataElement); } leftChild.setRight(node, dataElement); node.setParent(leftChild, dataElement); } /** * complicated red-black insert stuff. Based on Sun's TreeMap * implementation, though it's barely recognizable any more * * @param insertedNode the node to be inserted * @param dataElement the KEY or VALUE int */ private void doRedBlackInsert(final Node insertedNode, final DataElement dataElement) { Node currentNode = insertedNode; makeRed(currentNode, dataElement); while (currentNode != null && currentNode != rootNode[dataElement.ordinal()] && isRed(currentNode.getParent(dataElement), dataElement)) { if (currentNode.isLeftChild(dataElement)) { final Node y = getRightChild(getGrandParent(currentNode, dataElement), dataElement); if (isRed(y, dataElement)) { makeBlack(getParent(currentNode, dataElement), dataElement); makeBlack(y, dataElement); makeRed(getGrandParent(currentNode, dataElement), dataElement); currentNode = getGrandParent(currentNode, dataElement); } else { //dead code? if (currentNode.isRightChild(dataElement)) { currentNode = getParent(currentNode, dataElement); rotateLeft(currentNode, dataElement); } makeBlack(getParent(currentNode, dataElement), dataElement); makeRed(getGrandParent(currentNode, dataElement), dataElement); if (getGrandParent(currentNode, dataElement) != null) { rotateRight(getGrandParent(currentNode, dataElement), dataElement); } } } else { // just like clause above, except swap left for right final Node y = getLeftChild(getGrandParent(currentNode, dataElement), dataElement); if (isRed(y, dataElement)) { makeBlack(getParent(currentNode, dataElement), dataElement); makeBlack(y, dataElement); makeRed(getGrandParent(currentNode, dataElement), dataElement); currentNode = getGrandParent(currentNode, dataElement); } else { //dead code? if (currentNode.isLeftChild(dataElement)) { currentNode = getParent(currentNode, dataElement); rotateRight(currentNode, dataElement); } makeBlack(getParent(currentNode, dataElement), dataElement); makeRed(getGrandParent(currentNode, dataElement), dataElement); if (getGrandParent(currentNode, dataElement) != null) { rotateLeft(getGrandParent(currentNode, dataElement), dataElement); } } } } makeBlack(rootNode[dataElement.ordinal()], dataElement); } /** * complicated red-black delete stuff. Based on Sun's TreeMap * implementation, though it's barely recognizable any more * * @param deletedNode the node to be deleted */ private void doRedBlackDelete(final Node deletedNode) { for (final DataElement dataElement : DataElement.values()) { // if deleted node has both left and children, swap with // the next greater node if (deletedNode.getLeft(dataElement) != null && deletedNode.getRight(dataElement) != null) { swapPosition(nextGreater(deletedNode, dataElement), deletedNode, dataElement); } final Node replacement = deletedNode.getLeft(dataElement) != null ? deletedNode.getLeft(dataElement) : deletedNode.getRight(dataElement); if (replacement != null) { replacement.setParent(deletedNode.getParent(dataElement), dataElement); if (deletedNode.getParent(dataElement) == null) { rootNode[dataElement.ordinal()] = replacement; } else if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) { deletedNode.getParent(dataElement).setLeft(replacement, dataElement); } else { deletedNode.getParent(dataElement).setRight(replacement, dataElement); } deletedNode.setLeft(null, dataElement); deletedNode.setRight(null, dataElement); deletedNode.setParent(null, dataElement); if (isBlack(deletedNode, dataElement)) { doRedBlackDeleteFixup(replacement, dataElement); } } else { // replacement is null if (deletedNode.getParent(dataElement) == null) { // empty tree rootNode[dataElement.ordinal()] = null; } else { // deleted node had no children if (isBlack(deletedNode, dataElement)) { doRedBlackDeleteFixup(deletedNode, dataElement); } if (deletedNode.getParent(dataElement) != null) { if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) { deletedNode.getParent(dataElement).setLeft(null, dataElement); } else { deletedNode.getParent(dataElement).setRight(null, dataElement); } deletedNode.setParent(null, dataElement); } } } } shrink(); } /** * complicated red-black delete stuff. Based on Sun's TreeMap * implementation, though it's barely recognizable any more. This * rebalances the tree (somewhat, as red-black trees are not * perfectly balanced -- perfect balancing takes longer) * * @param replacementNode the node being replaced * @param dataElement the KEY or VALUE int */ private void doRedBlackDeleteFixup(final Node replacementNode, final DataElement dataElement) { Node currentNode = replacementNode; while (currentNode != rootNode[dataElement.ordinal()] && isBlack(currentNode, dataElement)) { if (currentNode.isLeftChild(dataElement)) { Node siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); if (isRed(siblingNode, dataElement)) { makeBlack(siblingNode, dataElement); makeRed(getParent(currentNode, dataElement), dataElement); rotateLeft(getParent(currentNode, dataElement), dataElement); siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); } if (isBlack(getLeftChild(siblingNode, dataElement), dataElement) && isBlack(getRightChild(siblingNode, dataElement), dataElement)) { makeRed(siblingNode, dataElement); currentNode = getParent(currentNode, dataElement); } else { if (isBlack(getRightChild(siblingNode, dataElement), dataElement)) { makeBlack(getLeftChild(siblingNode, dataElement), dataElement); makeRed(siblingNode, dataElement); rotateRight(siblingNode, dataElement); siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); } copyColor(getParent(currentNode, dataElement), siblingNode, dataElement); makeBlack(getParent(currentNode, dataElement), dataElement); makeBlack(getRightChild(siblingNode, dataElement), dataElement); rotateLeft(getParent(currentNode, dataElement), dataElement); currentNode = rootNode[dataElement.ordinal()]; } } else { Node siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); if (isRed(siblingNode, dataElement)) { makeBlack(siblingNode, dataElement); makeRed(getParent(currentNode, dataElement), dataElement); rotateRight(getParent(currentNode, dataElement), dataElement); siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); } if (isBlack(getRightChild(siblingNode, dataElement), dataElement) && isBlack(getLeftChild(siblingNode, dataElement), dataElement)) { makeRed(siblingNode, dataElement); currentNode = getParent(currentNode, dataElement); } else { if (isBlack(getLeftChild(siblingNode, dataElement), dataElement)) { makeBlack(getRightChild(siblingNode, dataElement), dataElement); makeRed(siblingNode, dataElement); rotateLeft(siblingNode, dataElement); siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); } copyColor(getParent(currentNode, dataElement), siblingNode, dataElement); makeBlack(getParent(currentNode, dataElement), dataElement); makeBlack(getLeftChild(siblingNode, dataElement), dataElement); rotateRight(getParent(currentNode, dataElement), dataElement); currentNode = rootNode[dataElement.ordinal()]; } } } makeBlack(currentNode, dataElement); } /** * swap two nodes (except for their content), taking care of * special cases where one is the other's parent ... hey, it * happens. * * @param x one node * @param y another node * @param dataElement the KEY or VALUE int */ private void swapPosition(final Node x, final Node y, final DataElement dataElement) { // Save initial values. final Node xFormerParent = x.getParent(dataElement); final Node xFormerLeftChild = x.getLeft(dataElement); final Node xFormerRightChild = x.getRight(dataElement); final Node yFormerParent = y.getParent(dataElement); final Node yFormerLeftChild = y.getLeft(dataElement); final Node yFormerRightChild = y.getRight(dataElement); final boolean xWasLeftChild = x.getParent(dataElement) != null && x == x.getParent(dataElement).getLeft(dataElement); final boolean yWasLeftChild = y.getParent(dataElement) != null && y == y.getParent(dataElement).getLeft(dataElement); // Swap, handling special cases of one being the other's parent. if (x == yFormerParent) { // x was y's parent x.setParent(y, dataElement); if (yWasLeftChild) { y.setLeft(x, dataElement); y.setRight(xFormerRightChild, dataElement); } else { y.setRight(x, dataElement); y.setLeft(xFormerLeftChild, dataElement); } } else { x.setParent(yFormerParent, dataElement); if (yFormerParent != null) { if (yWasLeftChild) { yFormerParent.setLeft(x, dataElement); } else { yFormerParent.setRight(x, dataElement); } } y.setLeft(xFormerLeftChild, dataElement); y.setRight(xFormerRightChild, dataElement); } if (y == xFormerParent) { // y was x's parent y.setParent(x, dataElement); if (xWasLeftChild) { x.setLeft(y, dataElement); x.setRight(yFormerRightChild, dataElement); } else { x.setRight(y, dataElement); x.setLeft(yFormerLeftChild, dataElement); } } else { y.setParent(xFormerParent, dataElement); if (xFormerParent != null) { if (xWasLeftChild) { xFormerParent.setLeft(y, dataElement); } else { xFormerParent.setRight(y, dataElement); } } x.setLeft(yFormerLeftChild, dataElement); x.setRight(yFormerRightChild, dataElement); } // Fix children's parent pointers if (x.getLeft(dataElement) != null) { x.getLeft(dataElement).setParent(x, dataElement); } if (x.getRight(dataElement) != null) { x.getRight(dataElement).setParent(x, dataElement); } if (y.getLeft(dataElement) != null) { y.getLeft(dataElement).setParent(y, dataElement); } if (y.getRight(dataElement) != null) { y.getRight(dataElement).setParent(y, dataElement); } x.swapColors(y, dataElement); // Check if root changed if (rootNode[dataElement.ordinal()] == x) { rootNode[dataElement.ordinal()] = y; } else if (rootNode[dataElement.ordinal()] == y) { rootNode[dataElement.ordinal()] = x; } } /** * check if an object is fit to be proper input ... has to be * Comparable and non-null * * @param o the object being checked * @param index the KEY or VALUE int (used to put the right word in the * exception message) * * @throws NullPointerException if o is null * @throws ClassCastException if o is not Comparable */ private static void checkNonNullComparable(final Object o, final DataElement dataElement) { if (o == null) { throw new NullPointerException(dataElement + " cannot be null"); } if (!(o instanceof Comparable)) { throw new ClassCastException(dataElement + " must be Comparable"); } } /** * check a key for validity (non-null and implements Comparable) * * @param key the key to be checked * * @throws NullPointerException if key is null * @throws ClassCastException if key is not Comparable */ private static void checkKey(final Object key) { checkNonNullComparable(key, KEY); } /** * check a value for validity (non-null and implements Comparable) * * @param value the value to be checked * * @throws NullPointerException if value is null * @throws ClassCastException if value is not Comparable */ private static void checkValue(final Object value) { checkNonNullComparable(value, VALUE); } /** * check a key and a value for validity (non-null and implements * Comparable) * * @param key the key to be checked * @param value the value to be checked * * @throws NullPointerException if key or value is null * @throws ClassCastException if key or value is not Comparable */ private static void checkKeyAndValue(final Object key, final Object value) { checkKey(key); checkValue(value); } /** * increment the modification count -- used to check for * concurrent modification of the map through the map and through * an Iterator from one of its Set or Collection views */ private void modify() { modifications++; } /** * bump up the size and note that the map has changed */ private void grow() { modify(); nodeCount++; } /** * decrement the size and note that the map has changed */ private void shrink() { modify(); nodeCount--; } /** * insert a node by its value * * @param newNode the node to be inserted * * @throws IllegalArgumentException if the node already exists * in the value mapping */ private void insertValue(final Node newNode) throws IllegalArgumentException { Node node = rootNode[VALUE.ordinal()]; while (true) { final int cmp = compare(newNode.getValue(), node.getValue()); if (cmp == 0) { throw new IllegalArgumentException( "Cannot store a duplicate value (\"" + newNode.getData(VALUE) + "\") in this Map"); } else if (cmp < 0) { if (node.getLeft(VALUE) != null) { node = node.getLeft(VALUE); } else { node.setLeft(newNode, VALUE); newNode.setParent(node, VALUE); doRedBlackInsert(newNode, VALUE); break; } } else { // cmp > 0 if (node.getRight(VALUE) != null) { node = node.getRight(VALUE); } else { node.setRight(newNode, VALUE); newNode.setParent(node, VALUE); doRedBlackInsert(newNode, VALUE); break; } } } } //----------------------------------------------------------------------- /** * Compares for equals as per the API. * * @param obj the object to compare to * @param type the KEY or VALUE int * @return true if equal */ private boolean doEquals(final Object obj, final DataElement dataElement) { if (obj == this) { return true; } if (obj instanceof Map == false) { return false; } final Map other = (Map) obj; if (other.size() != size()) { return false; } if (nodeCount > 0) { try { for (final MapIterator it = getMapIterator(dataElement); it.hasNext(); ) { final Object key = it.next(); final Object value = it.getValue(); if (value.equals(other.get(key)) == false) { return false; } } } catch (final ClassCastException ex) { return false; } catch (final NullPointerException ex) { return false; } } return true; } /** * Gets the hash code value for this map as per the API. * * @param type the KEY or VALUE int * @return the hash code value for this map */ private int doHashCode(final DataElement dataElement) { int total = 0; if (nodeCount > 0) { for (final MapIterator it = getMapIterator(dataElement); it.hasNext(); ) { final Object key = it.next(); final Object value = it.getValue(); total += key.hashCode() ^ value.hashCode(); } } return total; } /** * Gets the string form of this map as per AbstractMap. * * @param type the KEY or VALUE int * @return the string form of this map */ private String doToString(final DataElement dataElement) { if (nodeCount == 0) { return "{}"; } final StringBuilder buf = new StringBuilder(nodeCount * 32); buf.append('{'); final MapIterator it = getMapIterator(dataElement); boolean hasNext = it.hasNext(); while (hasNext) { final Object key = it.next(); final Object value = it.getValue(); buf.append(key == this ? "(this Map)" : key) .append('=') .append(value == this ? "(this Map)" : value); hasNext = it.hasNext(); if (hasNext) { buf.append(", "); } } buf.append('}'); return buf.toString(); } private MapIterator getMapIterator(final DataElement dataElement) { switch (dataElement) { case KEY: return new ViewMapIterator(KEY); case VALUE: return new InverseViewMapIterator(VALUE); default: throw new IllegalArgumentException(); } } /** * Reads the content of the stream. */ @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException{ stream.defaultReadObject(); rootNode = new Node[2]; int size = stream.readInt(); for(int i = 0; i < size; i++){ K k =(K) stream.readObject(); V v =(V) stream.readObject(); put(k, v); } } /** * Writes the content to the stream for serialization. */ private void writeObject(final ObjectOutputStream stream) throws IOException{ stream.defaultWriteObject(); stream.writeInt(this.size()); for (final Entry entry : entrySet()) { stream.writeObject(entry.getKey()); stream.writeObject(entry.getValue()); } } //----------------------------------------------------------------------- /** * A view of this map. */ abstract class View extends AbstractSet { /** Whether to return KEY or VALUE order. */ final DataElement orderType; /** * Constructor. * @param orderType the KEY or VALUE int for the order * @param main the main map */ View(final DataElement orderType) { super(); this.orderType = orderType; } @Override public int size() { return TreeBidiMap.this.size(); } @Override public void clear() { TreeBidiMap.this.clear(); } } class KeyView extends View { /** * Create a new TreeBidiMap.KeyView. */ public KeyView(final DataElement orderType) { super(orderType); } @Override public Iterator iterator() { return new ViewMapIterator(orderType); } @Override public boolean contains(final Object obj) { checkNonNullComparable(obj, KEY); return lookupKey(obj) != null; } @Override public boolean remove(final Object o) { return doRemoveKey(o) != null; } } class ValueView extends View { /** * Create a new TreeBidiMap.ValueView. */ public ValueView(final DataElement orderType) { super(orderType); } @Override public Iterator iterator() { return new InverseViewMapIterator(orderType); } @Override public boolean contains(final Object obj) { checkNonNullComparable(obj, VALUE); return lookupValue(obj) != null; } @Override public boolean remove(final Object o) { return doRemoveValue(o) != null; } } /** * A view of this map. */ class EntryView extends View> { EntryView() { super(KEY); } @Override public boolean contains(final Object obj) { if (obj instanceof Map.Entry == false) { return false; } final Map.Entry entry = (Map.Entry) obj; final Object value = entry.getValue(); final Node node = lookupKey(entry.getKey()); return node != null && node.getValue().equals(value); } @Override public boolean remove(final Object obj) { if (obj instanceof Map.Entry == false) { return false; } final Map.Entry entry = (Map.Entry) obj; final Object value = entry.getValue(); final Node node = lookupKey(entry.getKey()); if (node != null && node.getValue().equals(value)) { doRedBlackDelete(node); return true; } return false; } @Override public Iterator> iterator() { return new ViewMapEntryIterator(); } } /** * A view of this map. */ class InverseEntryView extends View> { InverseEntryView() { super(VALUE); } @Override public boolean contains(final Object obj) { if (obj instanceof Map.Entry == false) { return false; } final Map.Entry entry = (Map.Entry) obj; final Object value = entry.getValue(); final Node node = lookupValue(entry.getKey()); return node != null && node.getKey().equals(value); } @Override public boolean remove(final Object obj) { if (obj instanceof Map.Entry == false) { return false; } final Map.Entry entry = (Map.Entry) obj; final Object value = entry.getValue(); final Node node = lookupValue(entry.getKey()); if (node != null && node.getKey().equals(value)) { doRedBlackDelete(node); return true; } return false; } @Override public Iterator> iterator() { return new InverseViewMapEntryIterator(); } } //----------------------------------------------------------------------- /** * An iterator over the map. */ abstract class ViewIterator { /** Whether to return KEY or VALUE order. */ private final DataElement orderType; /** The last node returned by the iterator. */ Node lastReturnedNode; /** The next node to be returned by the iterator. */ private Node nextNode; /** The previous node in the sequence returned by the iterator. */ private Node previousNode; /** The modification count. */ private int expectedModifications; /** * Constructor. * @param orderType the KEY or VALUE int for the order * @param main the main map */ ViewIterator(final DataElement orderType) { super(); this.orderType = orderType; expectedModifications = modifications; nextNode = leastNode(rootNode[orderType.ordinal()], orderType); lastReturnedNode = null; previousNode = null; } public final boolean hasNext() { return nextNode != null; } protected Node navigateNext() { if (nextNode == null) { throw new NoSuchElementException(); } if (modifications != expectedModifications) { throw new ConcurrentModificationException(); } lastReturnedNode = nextNode; previousNode = nextNode; nextNode = nextGreater(nextNode, orderType); return lastReturnedNode; } public boolean hasPrevious() { return previousNode != null; } protected Node navigatePrevious() { if (previousNode == null) { throw new NoSuchElementException(); } if (modifications != expectedModifications) { throw new ConcurrentModificationException(); } nextNode = lastReturnedNode; if (nextNode == null) { nextNode = nextGreater(previousNode, orderType); } lastReturnedNode = previousNode; previousNode = nextSmaller(previousNode, orderType); return lastReturnedNode; } public final void remove() { if (lastReturnedNode == null) { throw new IllegalStateException(); } if (modifications != expectedModifications) { throw new ConcurrentModificationException(); } doRedBlackDelete(lastReturnedNode); expectedModifications++; lastReturnedNode = null; if (nextNode == null) { previousNode = greatestNode(rootNode[orderType.ordinal()], orderType); } else { previousNode = nextSmaller(nextNode, orderType); } } } //----------------------------------------------------------------------- /** * An iterator over the map. */ class ViewMapIterator extends ViewIterator implements OrderedMapIterator { /** * Constructor. */ ViewMapIterator(final DataElement orderType) { super(orderType); } public K getKey() { if (lastReturnedNode == null) { throw new IllegalStateException( "Iterator getKey() can only be called after next() and before remove()"); } return lastReturnedNode.getKey(); } public V getValue() { if (lastReturnedNode == null) { throw new IllegalStateException( "Iterator getValue() can only be called after next() and before remove()"); } return lastReturnedNode.getValue(); } public V setValue(final V obj) { throw new UnsupportedOperationException(); } public K next() { return navigateNext().getKey(); } public K previous() { return navigatePrevious().getKey(); } } /** * An iterator over the map. */ class InverseViewMapIterator extends ViewIterator implements OrderedMapIterator { /** * Create a new TreeBidiMap.InverseViewMapIterator. */ public InverseViewMapIterator(final DataElement orderType) { super(orderType); } public V getKey() { if (lastReturnedNode == null) { throw new IllegalStateException( "Iterator getKey() can only be called after next() and before remove()"); } return lastReturnedNode.getValue(); } public K getValue() { if (lastReturnedNode == null) { throw new IllegalStateException( "Iterator getValue() can only be called after next() and before remove()"); } return lastReturnedNode.getKey(); } public K setValue(final K obj) { throw new UnsupportedOperationException(); } public V next() { return navigateNext().getValue(); } public V previous() { return navigatePrevious().getValue(); } } /** * An iterator over the map entries. */ class ViewMapEntryIterator extends ViewIterator implements OrderedIterator> { /** * Constructor. */ ViewMapEntryIterator() { super(KEY); } public Map.Entry next() { return navigateNext(); } public Map.Entry previous() { return navigatePrevious(); } } /** * An iterator over the inverse map entries. */ class InverseViewMapEntryIterator extends ViewIterator implements OrderedIterator> { /** * Constructor. */ InverseViewMapEntryIterator() { super(VALUE); } public Map.Entry next() { return createEntry(navigateNext()); } public Map.Entry previous() { return createEntry(navigatePrevious()); } private Map.Entry createEntry(final Node node) { return new UnmodifiableMapEntry(node.getValue(), node.getKey()); } } //----------------------------------------------------------------------- //----------------------------------------------------------------------- /** * A node used to store the data. */ static class Node, V extends Comparable> implements Map.Entry, KeyValue { private final K key; private final V value; private final Node[] leftNode; private final Node[] rightNode; private final Node[] parentNode; private final boolean[] blackColor; private int hashcodeValue; private boolean calculatedHashCode; /** * Make a new cell with given key and value, and with null * links, and black (true) colors. * * @param key * @param value */ @SuppressWarnings("unchecked") Node(final K key, final V value) { super(); this.key = key; this.value = value; leftNode = new Node[2]; rightNode = new Node[2]; parentNode = new Node[2]; blackColor = new boolean[] { true, true }; calculatedHashCode = false; } private Object getData(final DataElement dataElement) { switch (dataElement) { case KEY: return getKey(); case VALUE: return getValue(); default: throw new IllegalArgumentException(); } } private void setLeft(final Node node, final DataElement dataElement) { leftNode[dataElement.ordinal()] = node; } private Node getLeft(final DataElement dataElement) { return leftNode[dataElement.ordinal()]; } private void setRight(final Node node, final DataElement dataElement) { rightNode[dataElement.ordinal()] = node; } private Node getRight(final DataElement dataElement) { return rightNode[dataElement.ordinal()]; } /** * Set this node's parent node. * * @param node the new parent node * @param index the KEY or VALUE int */ private void setParent(final Node node, final DataElement dataElement) { parentNode[dataElement.ordinal()] = node; } /** * Get the parent node. * * @param index the KEY or VALUE int * @return the parent node, may be null */ private Node getParent(final DataElement dataElement) { return parentNode[dataElement.ordinal()]; } /** * Exchange colors with another node. * * @param node the node to swap with * @param index the KEY or VALUE int */ private void swapColors(final Node node, final DataElement dataElement) { // Swap colors -- old hacker's trick blackColor[dataElement.ordinal()] ^= node.blackColor[dataElement.ordinal()]; node.blackColor[dataElement.ordinal()] ^= blackColor[dataElement.ordinal()]; blackColor[dataElement.ordinal()] ^= node.blackColor[dataElement.ordinal()]; } /** * Is this node black? * * @param index the KEY or VALUE int * @return true if black (which is represented as a true boolean) */ private boolean isBlack(final DataElement dataElement) { return blackColor[dataElement.ordinal()]; } /** * Is this node red? * * @param index the KEY or VALUE int * @return true if non-black */ private boolean isRed(final DataElement dataElement) { return !blackColor[dataElement.ordinal()]; } /** * Make this node black. * * @param index the KEY or VALUE int */ private void setBlack(final DataElement dataElement) { blackColor[dataElement.ordinal()] = true; } /** * Make this node red. * * @param index the KEY or VALUE int */ private void setRed(final DataElement dataElement) { blackColor[dataElement.ordinal()] = false; } /** * Make this node the same color as another * * @param node the node whose color we're adopting * @param index the KEY or VALUE int */ private void copyColor(final Node node, final DataElement dataElement) { blackColor[dataElement.ordinal()] = node.blackColor[dataElement.ordinal()]; } private boolean isLeftChild(final DataElement dataElement) { return parentNode[dataElement.ordinal()] != null && parentNode[dataElement.ordinal()].leftNode[dataElement.ordinal()] == this; } private boolean isRightChild(final DataElement dataElement) { return parentNode[dataElement.ordinal()] != null && parentNode[dataElement.ordinal()].rightNode[dataElement.ordinal()] == this; } //------------------------------------------------------------------- /** * Gets the key. * * @return the key corresponding to this entry. */ public K getKey() { return key; } /** * Gets the value. * * @return the value corresponding to this entry. */ public V getValue() { return value; } /** * Optional operation that is not permitted in this implementation * * @param ignored * @return does not return * @throws UnsupportedOperationException always */ public V setValue(final V ignored) throws UnsupportedOperationException { throw new UnsupportedOperationException("Map.Entry.setValue is not supported"); } /** * Compares the specified object with this entry for equality. * Returns true if the given object is also a map entry and * the two entries represent the same mapping. * * @param obj the object to be compared for equality with this entry. * @return true if the specified object is equal to this entry. */ @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof Map.Entry)) { return false; } final Map.Entry e = (Map.Entry) obj; return getKey().equals(e.getKey()) && getValue().equals(e.getValue()); } /** * @return the hash code value for this map entry. */ @Override public int hashCode() { if (!calculatedHashCode) { hashcodeValue = getKey().hashCode() ^ getValue().hashCode(); calculatedHashCode = true; } return hashcodeValue; } } //----------------------------------------------------------------------- /** * The inverse map implementation. */ class Inverse implements OrderedBidiMap { /** Store the keySet once created. */ private Set inverseKeySet; /** Store the valuesSet once created. */ private Set inverseValuesSet; /** Store the entrySet once created. */ private Set> inverseEntrySet; public int size() { return TreeBidiMap.this.size(); } public boolean isEmpty() { return TreeBidiMap.this.isEmpty(); } public K get(final Object key) { return TreeBidiMap.this.getKey(key); } public V getKey(final Object value) { return TreeBidiMap.this.get(value); } public boolean containsKey(final Object key) { return TreeBidiMap.this.containsValue(key); } public boolean containsValue(final Object value) { return TreeBidiMap.this.containsKey(value); } public V firstKey() { if (TreeBidiMap.this.nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } return leastNode(TreeBidiMap.this.rootNode[VALUE.ordinal()], VALUE).getValue(); } public V lastKey() { if (TreeBidiMap.this.nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } return greatestNode(TreeBidiMap.this.rootNode[VALUE.ordinal()], VALUE).getValue(); } public V nextKey(final V key) { checkKey(key); final Node node = nextGreater(TreeBidiMap.this.lookup(key, VALUE), VALUE); return node == null ? null : node.getValue(); } public V previousKey(final V key) { checkKey(key); final Node node = TreeBidiMap.this.nextSmaller(TreeBidiMap.this.lookup(key, VALUE), VALUE); return node == null ? null : node.getValue(); } public K put(final V key, final K value) { final K result = get(key); TreeBidiMap.this.doPut(value, key); return result; } public void putAll(final Map map) { for (final Map.Entry e : map.entrySet()) { put(e.getKey(), e.getValue()); } } public K remove(final Object key) { return TreeBidiMap.this.removeValue(key); } public V removeValue(final Object value) { return TreeBidiMap.this.remove(value); } public void clear() { TreeBidiMap.this.clear(); } public Set keySet() { if (inverseKeySet == null) { inverseKeySet = new ValueView(VALUE); } return inverseKeySet; } public Set values() { if (inverseValuesSet == null) { inverseValuesSet = new KeyView(VALUE); } return inverseValuesSet; } public Set> entrySet() { if (inverseEntrySet == null) { inverseEntrySet = new InverseEntryView(); } return inverseEntrySet; } public OrderedMapIterator mapIterator() { if (isEmpty()) { return EmptyOrderedMapIterator.emptyOrderedMapIterator(); } return new InverseViewMapIterator(VALUE); } public OrderedBidiMap inverseBidiMap() { return TreeBidiMap.this; } @Override public boolean equals(final Object obj) { return TreeBidiMap.this.doEquals(obj, DataElement.VALUE); } @Override public int hashCode() { return TreeBidiMap.this.doHashCode(DataElement.VALUE); } @Override public String toString() { return TreeBidiMap.this.doToString(DataElement.VALUE); } } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 24400; to = 24026.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.bidimap; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractSet; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import org.apache.commons.collections4.KeyValue; import org.apache.commons.collections4.MapIterator; import org.apache.commons.collections4.OrderedBidiMap; import org.apache.commons.collections4.OrderedIterator; import org.apache.commons.collections4.OrderedMapIterator; import org.apache.commons.collections4.iterators.EmptyOrderedMapIterator; import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry; import static org.apache.commons.collections4.bidimap.TreeBidiMap.DataElement.KEY; import static org.apache.commons.collections4.bidimap.TreeBidiMap.DataElement.VALUE; /** * Red-Black tree-based implementation of BidiMap where all objects added * implement the Comparable interface. *

* This class guarantees that the map will be in both ascending key order * and ascending value order, sorted according to the natural order for * the key's and value's classes. *

* This Map is intended for applications that need to be able to look * up a key-value pairing by either key or value, and need to do so * with equal efficiency. *

* While that goal could be accomplished by taking a pair of TreeMaps * and redirecting requests to the appropriate TreeMap (e.g., * containsKey would be directed to the TreeMap that maps values to * keys, containsValue would be directed to the TreeMap that maps keys * to values), there are problems with that implementation. * If the data contained in the TreeMaps is large, the cost of redundant * storage becomes significant. The {@link DualTreeBidiMap} and * {@link DualHashBidiMap} implementations use this approach. *

* This solution keeps minimizes the data storage by holding data only once. * The red-black algorithm is based on {@link java.util.TreeMap}, but has been modified * to simultaneously map a tree node by key and by value. This doubles the * cost of put operations (but so does using two TreeMaps), and nearly doubles * the cost of remove operations (there is a savings in that the lookup of the * node to be removed only has to be performed once). And since only one node * contains the key and value, storage is significantly less than that * required by two TreeMaps. *

* The Map.Entry instances returned by the appropriate methods will * not allow setValue() and will throw an * UnsupportedOperationException on attempts to call that method. * * @since 3.0 (previously DoubleOrderedMap v2.0) * @version $Id: TreeBidiMap.java 1543253 2013-11-19 00:43:25Z ggregory $ */ public class TreeBidiMap, V extends Comparable> implements OrderedBidiMap, Serializable { static enum DataElement { KEY("key"), VALUE("value"); private final String description; /** * Create a new TreeBidiMap.DataElement. * * @param description the description for the element */ private DataElement(final String description) { this.description = description; } @Override public String toString() { return description; } } private static final long serialVersionUID = 721969328361807L; private transient Node[] rootNode; private transient int nodeCount = 0; private transient int modifications = 0; private transient Set keySet; private transient Set valuesSet; private transient Set> entrySet; private transient Inverse inverse = null; //----------------------------------------------------------------------- /** * Constructs a new empty TreeBidiMap. */ @SuppressWarnings("unchecked") public TreeBidiMap() { super(); rootNode = new Node[2]; } /** * Constructs a new TreeBidiMap by copying an existing Map. * * @param map the map to copy * @throws ClassCastException if the keys/values in the map are * not Comparable or are not mutually comparable * @throws NullPointerException if any key or value in the map is null */ public TreeBidiMap(final Map map) { this(); putAll(map); } //----------------------------------------------------------------------- /** * Returns the number of key-value mappings in this map. * * @return the number of key-value mappings in this map */ public int size() { return nodeCount; } /** * Checks whether the map is empty or not. * * @return true if the map is empty */ public boolean isEmpty() { return nodeCount == 0; } /** * Checks whether this map contains the a mapping for the specified key. *

* The key must implement Comparable. * * @param key key whose presence in this map is to be tested * @return true if this map contains a mapping for the specified key * @throws ClassCastException if the key is of an inappropriate type * @throws NullPointerException if the key is null */ public boolean containsKey(final Object key) { checkKey(key); return lookupKey(key) != null; } /** * Checks whether this map contains the a mapping for the specified value. *

* The value must implement Comparable. * * @param value value whose presence in this map is to be tested * @return true if this map contains a mapping for the specified value * @throws ClassCastException if the value is of an inappropriate type * @throws NullPointerException if the value is null */ public boolean containsValue(final Object value) { checkValue(value); return lookupValue(value) != null; } /** * Gets the value to which this map maps the specified key. * Returns null if the map contains no mapping for this key. *

* The key must implement Comparable. * * @param key key whose associated value is to be returned * @return the value to which this map maps the specified key, * or null if the map contains no mapping for this key * @throws ClassCastException if the key is of an inappropriate type * @throws NullPointerException if the key is null */ public V get(final Object key) { checkKey(key); final Node node = lookupKey(key); return node == null ? null : node.getValue(); } /** * Puts the key-value pair into the map, replacing any previous pair. *

* When adding a key-value pair, the value may already exist in the map * against a different key. That mapping is removed, to ensure that the * value only occurs once in the inverse map. *

     *  BidiMap map1 = new TreeBidiMap();
     *  map.put("A","B");  // contains A mapped to B, as per Map
     *  map.put("A","C");  // contains A mapped to C, as per Map
     *
     *  BidiMap map2 = new TreeBidiMap();
     *  map.put("A","B");  // contains A mapped to B, as per Map
     *  map.put("C","B");  // contains C mapped to B, key A is removed
     * 
*

* Both key and value must implement Comparable. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value for the key * @throws ClassCastException if the key is of an inappropriate type * @throws NullPointerException if the key is null */ public V put(final K key, final V value) { final V result = get(key); doPut(key, value); return result; } /** * Puts all the mappings from the specified map into this map. *

* All keys and values must implement Comparable. * * @param map the map to copy from */ public void putAll(final Map map) { for (final Map.Entry e : map.entrySet()) { put(e.getKey(), e.getValue()); } } /** * Removes the mapping for this key from this map if present. *

* The key must implement Comparable. * * @param key key whose mapping is to be removed from the map. * @return previous value associated with specified key, * or null if there was no mapping for key. * @throws ClassCastException if the key is of an inappropriate type * @throws NullPointerException if the key is null */ public V remove(final Object key) { return doRemoveKey(key); } /** * Removes all mappings from this map. */ public void clear() { modify(); nodeCount = 0; rootNode[KEY.ordinal()] = null; rootNode[VALUE.ordinal()] = null; } //----------------------------------------------------------------------- /** * Returns the key to which this map maps the specified value. * Returns null if the map contains no mapping for this value. *

* The value must implement Comparable. * * @param value value whose associated key is to be returned. * @return the key to which this map maps the specified value, * or null if the map contains no mapping for this value. * @throws ClassCastException if the value is of an inappropriate type * @throws NullPointerException if the value is null */ public K getKey(final Object value) { checkValue(value); final Node node = lookupValue(value); return node == null ? null : node.getKey(); } /** * Removes the mapping for this value from this map if present. *

* The value must implement Comparable. * * @param value value whose mapping is to be removed from the map * @return previous key associated with specified value, * or null if there was no mapping for value. * @throws ClassCastException if the value is of an inappropriate type * @throws NullPointerException if the value is null */ public K removeValue(final Object value) { return doRemoveValue(value); } //----------------------------------------------------------------------- /** * Gets the first (lowest) key currently in this map. * * @return the first (lowest) key currently in this sorted map * @throws NoSuchElementException if this map is empty */ public K firstKey() { if (nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } return leastNode(rootNode[KEY.ordinal()], KEY).getKey(); } /** * Gets the last (highest) key currently in this map. * * @return the last (highest) key currently in this sorted map * @throws NoSuchElementException if this map is empty */ public K lastKey() { if (nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } return greatestNode(rootNode[KEY.ordinal()], KEY).getKey(); } /** * Gets the next key after the one specified. *

* The key must implement Comparable. * * @param key the key to search for next from * @return the next key, null if no match or at end */ public K nextKey(final K key) { checkKey(key); final Node node = nextGreater(lookupKey(key), KEY); return node == null ? null : node.getKey(); } /** * Gets the previous key before the one specified. *

* The key must implement Comparable. * * @param key the key to search for previous from * @return the previous key, null if no match or at start */ public K previousKey(final K key) { checkKey(key); final Node node = nextSmaller(lookupKey(key), KEY); return node == null ? null : node.getKey(); } //----------------------------------------------------------------------- /** * Returns a set view of the keys contained in this map in key order. *

* The set is backed by the map, so changes to the map are reflected in * the set, and vice-versa. If the map is modified while an iteration over * the set is in progress, the results of the iteration are undefined. *

* The set supports element removal, which removes the corresponding mapping * from the map. It does not support the add or addAll operations. * * @return a set view of the keys contained in this map. */ public Set keySet() { if (keySet == null) { keySet = new KeyView(KEY); } return keySet; } //----------------------------------------------------------------------- /** * Returns a set view of the values contained in this map in key order. * The returned object can be cast to a Set. *

* The set is backed by the map, so changes to the map are reflected in * the set, and vice-versa. If the map is modified while an iteration over * the set is in progress, the results of the iteration are undefined. *

* The set supports element removal, which removes the corresponding mapping * from the map. It does not support the add or addAll operations. * * @return a set view of the values contained in this map. */ public Set values() { if (valuesSet == null) { valuesSet = new ValueView(KEY); } return valuesSet; } //----------------------------------------------------------------------- /** * Returns a set view of the entries contained in this map in key order. * For simple iteration through the map, the MapIterator is quicker. *

* The set is backed by the map, so changes to the map are reflected in * the set, and vice-versa. If the map is modified while an iteration over * the set is in progress, the results of the iteration are undefined. *

* The set supports element removal, which removes the corresponding mapping * from the map. It does not support the add or addAll operations. * The returned MapEntry objects do not support setValue. * * @return a set view of the values contained in this map. */ public Set> entrySet() { if (entrySet == null) { entrySet = new EntryView(); } return entrySet; } //----------------------------------------------------------------------- public OrderedMapIterator mapIterator() { if (isEmpty()) { return EmptyOrderedMapIterator.emptyOrderedMapIterator(); } return new ViewMapIterator(KEY); } //----------------------------------------------------------------------- /** * Gets the inverse map for comparison. * * @return the inverse map */ public OrderedBidiMap inverseBidiMap() { if (inverse == null) { inverse = new Inverse(); } return inverse; } //----------------------------------------------------------------------- /** * Compares for equals as per the API. * * @param obj the object to compare to * @return true if equal */ @Override public boolean equals(final Object obj) { return this.doEquals(obj, KEY); } /** * Gets the hash code value for this map as per the API. * * @return the hash code value for this map */ @Override public int hashCode() { return this.doHashCode(KEY); } /** * Returns a string version of this Map in standard format. * * @return a standard format string version of the map */ @Override public String toString() { return this.doToString(KEY); } //----------------------------------------------------------------------- /** * Put logic. * * @param key the key, always the main map key * @param value the value, always the main map value */ private void doPut(final K key, final V value) { checkKeyAndValue(key, value); // store previous and remove previous mappings doRemoveKey(key); doRemoveValue(value); Node node = rootNode[KEY.ordinal()]; if (node == null) { // map is empty final Node root = new Node(key, value); rootNode[KEY.ordinal()] = root; rootNode[VALUE.ordinal()] = root; grow(); } else { // add new mapping while (true) { final int cmp = compare(key, node.getKey()); if (cmp == 0) { // shouldn't happen throw new IllegalArgumentException("Cannot store a duplicate key (\"" + key + "\") in this Map"); } else if (cmp < 0) { if (node.getLeft(KEY) != null) { node = node.getLeft(KEY); } else { final Node newNode = new Node(key, value); insertValue(newNode); node.setLeft(newNode, KEY); newNode.setParent(node, KEY); doRedBlackInsert(newNode, KEY); grow(); break; } } else { // cmp > 0 if (node.getRight(KEY) != null) { node = node.getRight(KEY); } else { final Node newNode = new Node(key, value); insertValue(newNode); node.setRight(newNode, KEY); newNode.setParent(node, KEY); doRedBlackInsert(newNode, KEY); grow(); break; } } } } } private V doRemoveKey(final Object key) { final Node node = lookupKey(key); if (node == null) { return null; } doRedBlackDelete(node); return node.getValue(); } private K doRemoveValue(final Object value) { final Node node = lookupValue(value); if (node == null) { return null; } doRedBlackDelete(node); return node.getKey(); } /** * do the actual lookup of a piece of data * * @param data the key or value to be looked up * @param index the KEY or VALUE int * @return the desired Node, or null if there is no mapping of the * specified data */ @SuppressWarnings("unchecked") private > Node lookup(final Object data, final DataElement dataElement) { Node rval = null; Node node = rootNode[dataElement.ordinal()]; while (node != null) { final int cmp = compare((T) data, (T) node.getData(dataElement)); if (cmp == 0) { rval = node; break; } else { node = cmp < 0 ? node.getLeft(dataElement) : node.getRight(dataElement); } } return rval; } private Node lookupKey(final Object key) { return this.lookup(key, KEY); } private Node lookupValue(final Object value) { return this.lookup(value, VALUE); } /** * get the next larger node from the specified node * * @param node the node to be searched from * @param index the KEY or VALUE int * @return the specified node */ private Node nextGreater(final Node node, final DataElement dataElement) { Node rval; if (node == null) { rval = null; } else if (node.getRight(dataElement) != null) { // everything to the node's right is larger. The least of // the right node's descendants is the next larger node rval = leastNode(node.getRight(dataElement), dataElement); } else { // traverse up our ancestry until we find an ancestor that // is null or one whose left child is our ancestor. If we // find a null, then this node IS the largest node in the // tree, and there is no greater node. Otherwise, we are // the largest node in the subtree on that ancestor's left // ... and that ancestor is the next greatest node Node parent = node.getParent(dataElement); Node child = node; while (parent != null && child == parent.getRight(dataElement)) { child = parent; parent = parent.getParent(dataElement); } rval = parent; } return rval; } /** * get the next larger node from the specified node * * @param node the node to be searched from * @param index the KEY or VALUE int * @return the specified node */ private Node nextSmaller(final Node node, final DataElement dataElement) { Node rval; if (node == null) { rval = null; } else if (node.getLeft(dataElement) != null) { // everything to the node's left is smaller. The greatest of // the left node's descendants is the next smaller node rval = greatestNode(node.getLeft(dataElement), dataElement); } else { // traverse up our ancestry until we find an ancestor that // is null or one whose right child is our ancestor. If we // find a null, then this node IS the largest node in the // tree, and there is no greater node. Otherwise, we are // the largest node in the subtree on that ancestor's right // ... and that ancestor is the next greatest node Node parent = node.getParent(dataElement); Node child = node; while (parent != null && child == parent.getLeft(dataElement)) { child = parent; parent = parent.getParent(dataElement); } rval = parent; } return rval; } //----------------------------------------------------------------------- /** * Compare two objects * * @param o1 the first object * @param o2 the second object * * @return negative value if o1 < o2; 0 if o1 == o2; positive * value if o1 > o2 */ private static > int compare(final T o1, final T o2) { return o1.compareTo(o2); } /** * Find the least node from a given node. * * @param node the node from which we will start searching * @param index the KEY or VALUE int * @return the smallest node, from the specified node, in the * specified mapping */ private Node leastNode(final Node node, final DataElement dataElement) { Node rval = node; if (rval != null) { while (rval.getLeft(dataElement) != null) { rval = rval.getLeft(dataElement); } } return rval; } /** * Find the greatest node from a given node. * * @param node the node from which we will start searching * @param index the KEY or VALUE int * @return the greatest node, from the specified node */ private Node greatestNode(final Node node, final DataElement dataElement) { Node rval = node; if (rval != null) { while (rval.getRight(dataElement) != null) { rval = rval.getRight(dataElement); } } return rval; } /** * copy the color from one node to another, dealing with the fact * that one or both nodes may, in fact, be null * * @param from the node whose color we're copying; may be null * @param to the node whose color we're changing; may be null * @param index the KEY or VALUE int */ private void copyColor(final Node from, final Node to, final DataElement dataElement) { if (to != null) { if (from == null) { // by default, make it black to.setBlack(dataElement); } else { to.copyColor(from, dataElement); } } } /** * is the specified node red? if the node does not exist, no, it's * black, thank you * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private static boolean isRed(final Node node, final DataElement dataElement) { return node != null && node.isRed(dataElement); } /** * is the specified black red? if the node does not exist, sure, * it's black, thank you * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private static boolean isBlack(final Node node, final DataElement dataElement) { return node == null || node.isBlack(dataElement); } /** * force a node (if it exists) red * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private static void makeRed(final Node node, final DataElement dataElement) { if (node != null) { node.setRed(dataElement); } } /** * force a node (if it exists) black * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private static void makeBlack(final Node node, final DataElement dataElement) { if (node != null) { node.setBlack(dataElement); } } /** * get a node's grandparent. mind you, the node, its parent, or * its grandparent may not exist. no problem * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private Node getGrandParent(final Node node, final DataElement dataElement) { return getParent(getParent(node, dataElement), dataElement); } /** * get a node's parent. mind you, the node, or its parent, may not * exist. no problem * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private Node getParent(final Node node, final DataElement dataElement) { return node == null ? null : node.getParent(dataElement); } /** * get a node's right child. mind you, the node may not exist. no * problem * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private Node getRightChild(final Node node, final DataElement dataElement) { return node == null ? null : node.getRight(dataElement); } /** * get a node's left child. mind you, the node may not exist. no * problem * * @param node the node (may be null) in question * @param index the KEY or VALUE int */ private Node getLeftChild(final Node node, final DataElement dataElement) { return node == null ? null : node.getLeft(dataElement); } /** * do a rotate left. standard fare in the world of balanced trees * * @param node the node to be rotated * @param index the KEY or VALUE int */ private void rotateLeft(final Node node, final DataElement dataElement) { final Node rightChild = node.getRight(dataElement); node.setRight(rightChild.getLeft(dataElement), dataElement); if (rightChild.getLeft(dataElement) != null) { rightChild.getLeft(dataElement).setParent(node, dataElement); } rightChild.setParent(node.getParent(dataElement), dataElement); if (node.getParent(dataElement) == null) { // node was the root ... now its right child is the root rootNode[dataElement.ordinal()] = rightChild; } else if (node.getParent(dataElement).getLeft(dataElement) == node) { node.getParent(dataElement).setLeft(rightChild, dataElement); } else { node.getParent(dataElement).setRight(rightChild, dataElement); } rightChild.setLeft(node, dataElement); node.setParent(rightChild, dataElement); } /** * do a rotate right. standard fare in the world of balanced trees * * @param node the node to be rotated * @param index the KEY or VALUE int */ private void rotateRight(final Node node, final DataElement dataElement) { final Node leftChild = node.getLeft(dataElement); node.setLeft(leftChild.getRight(dataElement), dataElement); if (leftChild.getRight(dataElement) != null) { leftChild.getRight(dataElement).setParent(node, dataElement); } leftChild.setParent(node.getParent(dataElement), dataElement); if (node.getParent(dataElement) == null) { // node was the root ... now its left child is the root rootNode[dataElement.ordinal()] = leftChild; } else if (node.getParent(dataElement).getRight(dataElement) == node) { node.getParent(dataElement).setRight(leftChild, dataElement); } else { node.getParent(dataElement).setLeft(leftChild, dataElement); } leftChild.setRight(node, dataElement); node.setParent(leftChild, dataElement); } /** * complicated red-black insert stuff. Based on Sun's TreeMap * implementation, though it's barely recognizable any more * * @param insertedNode the node to be inserted * @param dataElement the KEY or VALUE int */ private void doRedBlackInsert(final Node insertedNode, final DataElement dataElement) { Node currentNode = insertedNode; makeRed(currentNode, dataElement); while (currentNode != null && currentNode != rootNode[dataElement.ordinal()] && isRed(currentNode.getParent(dataElement), dataElement)) { if (currentNode.isLeftChild(dataElement)) { final Node y = getRightChild(getGrandParent(currentNode, dataElement), dataElement); if (isRed(y, dataElement)) { makeBlack(getParent(currentNode, dataElement), dataElement); makeBlack(y, dataElement); makeRed(getGrandParent(currentNode, dataElement), dataElement); currentNode = getGrandParent(currentNode, dataElement); } else { //dead code? if (currentNode.isRightChild(dataElement)) { currentNode = getParent(currentNode, dataElement); rotateLeft(currentNode, dataElement); } makeBlack(getParent(currentNode, dataElement), dataElement); makeRed(getGrandParent(currentNode, dataElement), dataElement); if (getGrandParent(currentNode, dataElement) != null) { rotateRight(getGrandParent(currentNode, dataElement), dataElement); } } } else { // just like clause above, except swap left for right final Node y = getLeftChild(getGrandParent(currentNode, dataElement), dataElement); if (isRed(y, dataElement)) { makeBlack(getParent(currentNode, dataElement), dataElement); makeBlack(y, dataElement); makeRed(getGrandParent(currentNode, dataElement), dataElement); currentNode = getGrandParent(currentNode, dataElement); } else { //dead code? if (currentNode.isLeftChild(dataElement)) { currentNode = getParent(currentNode, dataElement); rotateRight(currentNode, dataElement); } makeBlack(getParent(currentNode, dataElement), dataElement); makeRed(getGrandParent(currentNode, dataElement), dataElement); if (getGrandParent(currentNode, dataElement) != null) { rotateLeft(getGrandParent(currentNode, dataElement), dataElement); } } } } makeBlack(rootNode[dataElement.ordinal()], dataElement); } /** * complicated red-black delete stuff. Based on Sun's TreeMap * implementation, though it's barely recognizable any more * * @param deletedNode the node to be deleted */ private void doRedBlackDelete(final Node deletedNode) { for (final DataElement dataElement : DataElement.values()) { // if deleted node has both left and children, swap with // the next greater node if (deletedNode.getLeft(dataElement) != null && deletedNode.getRight(dataElement) != null) { swapPosition(nextGreater(deletedNode, dataElement), deletedNode, dataElement); } final Node replacement = deletedNode.getLeft(dataElement) != null ? deletedNode.getLeft(dataElement) : deletedNode.getRight(dataElement); if (replacement != null) { replacement.setParent(deletedNode.getParent(dataElement), dataElement); if (deletedNode.getParent(dataElement) == null) { rootNode[dataElement.ordinal()] = replacement; } else if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) { deletedNode.getParent(dataElement).setLeft(replacement, dataElement); } else { deletedNode.getParent(dataElement).setRight(replacement, dataElement); } deletedNode.setLeft(null, dataElement); deletedNode.setRight(null, dataElement); deletedNode.setParent(null, dataElement); if (isBlack(deletedNode, dataElement)) { doRedBlackDeleteFixup(replacement, dataElement); } } else { // replacement is null if (deletedNode.getParent(dataElement) == null) { // empty tree rootNode[dataElement.ordinal()] = null; } else { // deleted node had no children if (isBlack(deletedNode, dataElement)) { doRedBlackDeleteFixup(deletedNode, dataElement); } if (deletedNode.getParent(dataElement) != null) { if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) { deletedNode.getParent(dataElement).setLeft(null, dataElement); } else { deletedNode.getParent(dataElement).setRight(null, dataElement); } deletedNode.setParent(null, dataElement); } } } } shrink(); } /** * complicated red-black delete stuff. Based on Sun's TreeMap * implementation, though it's barely recognizable any more. This * rebalances the tree (somewhat, as red-black trees are not * perfectly balanced -- perfect balancing takes longer) * * @param replacementNode the node being replaced * @param dataElement the KEY or VALUE int */ private void doRedBlackDeleteFixup(final Node replacementNode, final DataElement dataElement) { Node currentNode = replacementNode; while (currentNode != rootNode[dataElement.ordinal()] && isBlack(currentNode, dataElement)) { if (currentNode.isLeftChild(dataElement)) { Node siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); if (isRed(siblingNode, dataElement)) { makeBlack(siblingNode, dataElement); makeRed(getParent(currentNode, dataElement), dataElement); rotateLeft(getParent(currentNode, dataElement), dataElement); siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); } if (isBlack(getLeftChild(siblingNode, dataElement), dataElement) && isBlack(getRightChild(siblingNode, dataElement), dataElement)) { makeRed(siblingNode, dataElement); currentNode = getParent(currentNode, dataElement); } else { if (isBlack(getRightChild(siblingNode, dataElement), dataElement)) { makeBlack(getLeftChild(siblingNode, dataElement), dataElement); makeRed(siblingNode, dataElement); rotateRight(siblingNode, dataElement); siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); } copyColor(getParent(currentNode, dataElement), siblingNode, dataElement); makeBlack(getParent(currentNode, dataElement), dataElement); makeBlack(getRightChild(siblingNode, dataElement), dataElement); rotateLeft(getParent(currentNode, dataElement), dataElement); currentNode = rootNode[dataElement.ordinal()]; } } else { Node siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); if (isRed(siblingNode, dataElement)) { makeBlack(siblingNode, dataElement); makeRed(getParent(currentNode, dataElement), dataElement); rotateRight(getParent(currentNode, dataElement), dataElement); siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); } if (isBlack(getRightChild(siblingNode, dataElement), dataElement) && isBlack(getLeftChild(siblingNode, dataElement), dataElement)) { makeRed(siblingNode, dataElement); currentNode = getParent(currentNode, dataElement); } else { if (isBlack(getLeftChild(siblingNode, dataElement), dataElement)) { makeBlack(getRightChild(siblingNode, dataElement), dataElement); makeRed(siblingNode, dataElement); rotateLeft(siblingNode, dataElement); siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); } copyColor(getParent(currentNode, dataElement), siblingNode, dataElement); makeBlack(getParent(currentNode, dataElement), dataElement); makeBlack(getLeftChild(siblingNode, dataElement), dataElement); rotateRight(getParent(currentNode, dataElement), dataElement); currentNode = rootNode[dataElement.ordinal()]; } } } makeBlack(currentNode, dataElement); } /** * swap two nodes (except for their content), taking care of * special cases where one is the other's parent ... hey, it * happens. * * @param x one node * @param y another node * @param dataElement the KEY or VALUE int */ private void swapPosition(final Node x, final Node y, final DataElement dataElement) { // Save initial values. final Node xFormerParent = x.getParent(dataElement); final Node xFormerLeftChild = x.getLeft(dataElement); final Node xFormerRightChild = x.getRight(dataElement); final Node yFormerParent = y.getParent(dataElement); final Node yFormerLeftChild = y.getLeft(dataElement); final Node yFormerRightChild = y.getRight(dataElement); final boolean xWasLeftChild = x.getParent(dataElement) != null && x == x.getParent(dataElement).getLeft(dataElement); final boolean yWasLeftChild = y.getParent(dataElement) != null && y == y.getParent(dataElement).getLeft(dataElement); // Swap, handling special cases of one being the other's parent. if (x == yFormerParent) { // x was y's parent x.setParent(y, dataElement); if (yWasLeftChild) { y.setLeft(x, dataElement); y.setRight(xFormerRightChild, dataElement); } else { y.setRight(x, dataElement); y.setLeft(xFormerLeftChild, dataElement); } } else { x.setParent(yFormerParent, dataElement); if (yFormerParent != null) { if (yWasLeftChild) { yFormerParent.setLeft(x, dataElement); } else { yFormerParent.setRight(x, dataElement); } } y.setLeft(xFormerLeftChild, dataElement); y.setRight(xFormerRightChild, dataElement); } if (y == xFormerParent) { // y was x's parent y.setParent(x, dataElement); if (xWasLeftChild) { x.setLeft(y, dataElement); x.setRight(yFormerRightChild, dataElement); } else { x.setRight(y, dataElement); x.setLeft(yFormerLeftChild, dataElement); } } else { y.setParent(xFormerParent, dataElement); if (xFormerParent != null) { if (xWasLeftChild) { xFormerParent.setLeft(y, dataElement); } else { xFormerParent.setRight(y, dataElement); } } x.setLeft(yFormerLeftChild, dataElement); x.setRight(yFormerRightChild, dataElement); } // Fix children's parent pointers if (x.getLeft(dataElement) != null) { x.getLeft(dataElement).setParent(x, dataElement); } if (x.getRight(dataElement) != null) { x.getRight(dataElement).setParent(x, dataElement); } if (y.getLeft(dataElement) != null) { y.getLeft(dataElement).setParent(y, dataElement); } if (y.getRight(dataElement) != null) { y.getRight(dataElement).setParent(y, dataElement); } x.swapColors(y, dataElement); // Check if root changed if (rootNode[dataElement.ordinal()] == x) { rootNode[dataElement.ordinal()] = y; } else if (rootNode[dataElement.ordinal()] == y) { rootNode[dataElement.ordinal()] = x; } } /** * check if an object is fit to be proper input ... has to be * Comparable and non-null * * @param o the object being checked * @param index the KEY or VALUE int (used to put the right word in the * exception message) * * @throws NullPointerException if o is null * @throws ClassCastException if o is not Comparable */ private static void checkNonNullComparable(final Object o, final DataElement dataElement) { if (o == null) { throw new NullPointerException(dataElement + " cannot be null"); } if (!(o instanceof Comparable)) { throw new ClassCastException(dataElement + " must be Comparable"); } } /** * check a key for validity (non-null and implements Comparable) * * @param key the key to be checked * * @throws NullPointerException if key is null * @throws ClassCastException if key is not Comparable */ private static void checkKey(final Object key) { checkNonNullComparable(key, KEY); } /** * check a value for validity (non-null and implements Comparable) * * @param value the value to be checked * * @throws NullPointerException if value is null * @throws ClassCastException if value is not Comparable */ private static void checkValue(final Object value) { checkNonNullComparable(value, VALUE); } /** * check a key and a value for validity (non-null and implements * Comparable) * * @param key the key to be checked * @param value the value to be checked * * @throws NullPointerException if key or value is null * @throws ClassCastException if key or value is not Comparable */ private static void checkKeyAndValue(final Object key, final Object value) { checkKey(key); checkValue(value); } /** * increment the modification count -- used to check for * concurrent modification of the map through the map and through * an Iterator from one of its Set or Collection views */ private void modify() { modifications++; } /** * bump up the size and note that the map has changed */ private void grow() { modify(); nodeCount++; } /** * decrement the size and note that the map has changed */ private void shrink() { modify(); nodeCount--; } /** * insert a node by its value * * @param newNode the node to be inserted * * @throws IllegalArgumentException if the node already exists * in the value mapping */ private void insertValue(final Node newNode) throws IllegalArgumentException { Node node = rootNode[VALUE.ordinal()]; while (true) { final int cmp = compare(newNode.getValue(), node.getValue()); if (cmp == 0) { throw new IllegalArgumentException( "Cannot store a duplicate value (\"" + newNode.getData(VALUE) + "\") in this Map"); } else if (cmp < 0) { if (node.getLeft(VALUE) != null) { node = node.getLeft(VALUE); } else { node.setLeft(newNode, VALUE); newNode.setParent(node, VALUE); doRedBlackInsert(newNode, VALUE); break; } } else { // cmp > 0 if (node.getRight(VALUE) != null) { node = node.getRight(VALUE); } else { node.setRight(newNode, VALUE); newNode.setParent(node, VALUE); doRedBlackInsert(newNode, VALUE); break; } } } } //----------------------------------------------------------------------- /** * Compares for equals as per the API. * * @param obj the object to compare to * @param type the KEY or VALUE int * @return true if equal */ private boolean doEquals(final Object obj, final DataElement dataElement) { if (obj == this) { return true; } if (obj instanceof Map == false) { return false; } final Map other = (Map) obj; if (other.size() != size()) { return false; } if (nodeCount > 0) { try { for (final MapIterator it = getMapIterator(dataElement); it.hasNext(); ) { final Object key = it.next(); final Object value = it.getValue(); if (value.equals(other.get(key)) == false) { return false; } } } catch (final ClassCastException ex) { return false; } catch (final NullPointerException ex) { return false; } } return true; } /** * Gets the hash code value for this map as per the API. * * @param type the KEY or VALUE int * @return the hash code value for this map */ private int doHashCode(final DataElement dataElement) { int total = 0; if (nodeCount > 0) { for (final MapIterator it = getMapIterator(dataElement); it.hasNext(); ) { final Object key = it.next(); final Object value = it.getValue(); total += key.hashCode() ^ value.hashCode(); } } return total; } /** * Gets the string form of this map as per AbstractMap. * * @param type the KEY or VALUE int * @return the string form of this map */ private String doToString(final DataElement dataElement) { if (nodeCount == 0) { return "{}"; } final StringBuilder buf = new StringBuilder(nodeCount * 32); buf.append('{'); final MapIterator it = getMapIterator(dataElement); boolean hasNext = it.hasNext(); while (hasNext) { final Object key = it.next(); final Object value = it.getValue(); buf.append(key == this ? "(this Map)" : key) .append('=') .append(value == this ? "(this Map)" : value); hasNext = it.hasNext(); if (hasNext) { buf.append(", "); } } buf.append('}'); return buf.toString(); } private MapIterator getMapIterator(final DataElement dataElement) { switch (dataElement) { case KEY: return new ViewMapIterator(KEY); case VALUE: return new InverseViewMapIterator(VALUE); default: throw new IllegalArgumentException(); } } /** * Reads the content of the stream. */ @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException{ stream.defaultReadObject(); rootNode = new Node[2]; int size = stream.readInt(); for(int i = 0; i < size; i++){ K k =(K) stream.readObject(); V v =(V) stream.readObject(); put(k, v); } } /** * Writes the content to the stream for serialization. */ private void writeObject(final ObjectOutputStream stream) throws IOException{ stream.defaultWriteObject(); stream.writeInt(this.size()); for (final Entry entry : entrySet()) { stream.writeObject(entry.getKey()); stream.writeObject(entry.getValue()); } } //----------------------------------------------------------------------- /** * A view of this map. */ abstract class View extends AbstractSet { /** Whether to return KEY or VALUE order. */ final DataElement orderType; /** * Constructor. * @param orderType the KEY or VALUE int for the order * @param main the main map */ View(final DataElement orderType) { super(); this.orderType = orderType; } @Override public int size() { return TreeBidiMap.this.size(); } @Override public void clear() { TreeBidiMap.this.clear(); } } class KeyView extends View { /** * Create a new TreeBidiMap.KeyView. */ public KeyView(final DataElement orderType) { super(orderType); } @Override public Iterator iterator() { return new ViewMapIterator(orderType); } @Override public boolean contains(final Object obj) { checkNonNullComparable(obj, KEY); return lookupKey(obj) != null; } @Override public boolean remove(final Object o) { return doRemoveKey(o) != null; } } class ValueView extends View { /** * Create a new TreeBidiMap.ValueView. */ public ValueView(final DataElement orderType) { super(orderType); } @Override public Iterator iterator() { return new InverseViewMapIterator(orderType); } @Override public boolean contains(final Object obj) { checkNonNullComparable(obj, VALUE); return lookupValue(obj) != null; } @Override public boolean remove(final Object o) { return doRemoveValue(o) != null; } } /** * A view of this map. */ class EntryView extends View> { EntryView() { super(KEY); } @Override public boolean contains(final Object obj) { if (obj instanceof Map.Entry == false) { return false; } final Map.Entry entry = (Map.Entry) obj; final Object value = entry.getValue(); final Node node = lookupKey(entry.getKey()); return node != null && node.getValue().equals(value); } @Override public boolean remove(final Object obj) { if (obj instanceof Map.Entry == false) { return false; } final Map.Entry entry = (Map.Entry) obj; final Object value = entry.getValue(); final Node node = lookupKey(entry.getKey()); if (node != null && node.getValue().equals(value)) { doRedBlackDelete(node); return true; } return false; } @Override public Iterator> iterator() { return new ViewMapEntryIterator(); } } /** * A view of this map. */ class InverseEntryView extends View> { InverseEntryView() { super(VALUE); } @Override public boolean contains(final Object obj) { if (obj instanceof Map.Entry == false) { return false; } final Map.Entry entry = (Map.Entry) obj; final Object value = entry.getValue(); final Node node = lookupValue(entry.getKey()); return node != null && node.getKey().equals(value); } @Override public boolean remove(final Object obj) { if (obj instanceof Map.Entry == false) { return false; } final Map.Entry entry = (Map.Entry) obj; final Object value = entry.getValue(); final Node node = lookupValue(entry.getKey()); if (node != null && node.getKey().equals(value)) { doRedBlackDelete(node); return true; } return false; } @Override public Iterator> iterator() { return new InverseViewMapEntryIterator(); } } //----------------------------------------------------------------------- /** * An iterator over the map. */ abstract class ViewIterator { /** Whether to return KEY or VALUE order. */ private final DataElement orderType; /** The last node returned by the iterator. */ Node lastReturnedNode; /** The next node to be returned by the iterator. */ private Node nextNode; /** The previous node in the sequence returned by the iterator. */ private Node previousNode; /** The modification count. */ private int expectedModifications; /** * Constructor. * @param orderType the KEY or VALUE int for the order * @param main the main map */ ViewIterator(final DataElement orderType) { super(); this.orderType = orderType; expectedModifications = modifications; nextNode = leastNode(rootNode[orderType.ordinal()], orderType); lastReturnedNode = null; previousNode = null; } public final boolean hasNext() { return nextNode != null; } protected Node navigateNext() { if (nextNode == null) { throw new NoSuchElementException(); } if (modifications != expectedModifications) { throw new ConcurrentModificationException(); } lastReturnedNode = nextNode; previousNode = nextNode; nextNode = nextGreater(nextNode, orderType); return lastReturnedNode; } public boolean hasPrevious() { return previousNode != null; } protected Node navigatePrevious() { if (previousNode == null) { throw new NoSuchElementException(); } if (modifications != expectedModifications) { throw new ConcurrentModificationException(); } nextNode = lastReturnedNode; if (nextNode == null) { nextNode = nextGreater(previousNode, orderType); } lastReturnedNode = previousNode; previousNode = nextSmaller(previousNode, orderType); return lastReturnedNode; } public final void remove() { if (lastReturnedNode == null) { throw new IllegalStateException(); } if (modifications != expectedModifications) { throw new ConcurrentModificationException(); } doRedBlackDelete(lastReturnedNode); expectedModifications++; lastReturnedNode = null; if (nextNode == null) { previousNode = greatestNode(rootNode[orderType.ordinal()], orderType); } else { previousNode = nextSmaller(nextNode, orderType); } } } //----------------------------------------------------------------------- /** * An iterator over the map. */ class ViewMapIterator extends ViewIterator implements OrderedMapIterator { /** * Constructor. */ ViewMapIterator(final DataElement orderType) { super(orderType); } public K getKey() { if (lastReturnedNode == null) { throw new IllegalStateException( "Iterator getKey() can only be called after next() and before remove()"); } return lastReturnedNode.getKey(); } public V getValue() { if (lastReturnedNode == null) { throw new IllegalStateException( "Iterator getValue() can only be called after next() and before remove()"); } return lastReturnedNode.getValue(); } public V setValue(final V obj) { throw new UnsupportedOperationException(); } public K next() { return navigateNext().getKey(); } public K previous() { return navigatePrevious().getKey(); } } /** * An iterator over the map. */ class InverseViewMapIterator extends ViewIterator implements OrderedMapIterator { /** * Create a new TreeBidiMap.InverseViewMapIterator. */ public InverseViewMapIterator(final DataElement orderType) { super(orderType); } public V getKey() { if (lastReturnedNode == null) { throw new IllegalStateException( "Iterator getKey() can only be called after next() and before remove()"); } return lastReturnedNode.getValue(); } public K getValue() { if (lastReturnedNode == null) { throw new IllegalStateException( "Iterator getValue() can only be called after next() and before remove()"); } return lastReturnedNode.getKey(); } public K setValue(final K obj) { throw new UnsupportedOperationException(); } public V next() { return navigateNext().getValue(); } public V previous() { return navigatePrevious().getValue(); } } /** * An iterator over the map entries. */ class ViewMapEntryIterator extends ViewIterator implements OrderedIterator> { /** * Constructor. */ ViewMapEntryIterator() { super(KEY); } public Map.Entry next() { return navigateNext(); } public Map.Entry previous() { return navigatePrevious(); } } /** * An iterator over the inverse map entries. */ class InverseViewMapEntryIterator extends ViewIterator implements OrderedIterator> { /** * Constructor. */ InverseViewMapEntryIterator() { super(VALUE); } public Map.Entry next() { return createEntry(navigateNext()); } public Map.Entry previous() { return createEntry(navigatePrevious()); } private Map.Entry createEntry(final Node node) { return new UnmodifiableMapEntry(node.getValue(), node.getKey()); } } //----------------------------------------------------------------------- //----------------------------------------------------------------------- /** * A node used to store the data. */ static class Node, V extends Comparable> implements Map.Entry, KeyValue { private final K key; private final V value; private final Node[] leftNode; private final Node[] rightNode; private final Node[] parentNode; private final boolean[] blackColor; private int hashcodeValue; private boolean calculatedHashCode; /** * Make a new cell with given key and value, and with null * links, and black (true) colors. * * @param key * @param value */ @SuppressWarnings("unchecked") Node(final K key, final V value) { super(); this.key = key; this.value = value; leftNode = new Node[2]; rightNode = new Node[2]; parentNode = new Node[2]; blackColor = new boolean[] { true, true }; calculatedHashCode = false; } private Object getData(final DataElement dataElement) { switch (dataElement) { case KEY: return getKey(); case VALUE: return getValue(); default: throw new IllegalArgumentException(); } } private void setLeft(final Node node, final DataElement dataElement) { leftNode[dataElement.ordinal()] = node; } private Node getLeft(final DataElement dataElement) { return leftNode[dataElement.ordinal()]; } private void setRight(final Node node, final DataElement dataElement) { rightNode[dataElement.ordinal()] = node; } private Node getRight(final DataElement dataElement) { return rightNode[dataElement.ordinal()]; } /** * Set this node's parent node. * * @param node the new parent node * @param index the KEY or VALUE int */ private void setParent(final Node node, final DataElement dataElement) { parentNode[dataElement.ordinal()] = node; } /** * Get the parent node. * * @param index the KEY or VALUE int * @return the parent node, may be null */ private Node getParent(final DataElement dataElement) { return parentNode[dataElement.ordinal()]; } /** * Exchange colors with another node. * * @param node the node to swap with * @param index the KEY or VALUE int */ private void swapColors(final Node node, final DataElement dataElement) { // Swap colors -- old hacker's trick blackColor[dataElement.ordinal()] ^= node.blackColor[dataElement.ordinal()]; node.blackColor[dataElement.ordinal()] ^= blackColor[dataElement.ordinal()]; blackColor[dataElement.ordinal()] ^= node.blackColor[dataElement.ordinal()]; } /** * Is this node black? * * @param index the KEY or VALUE int * @return true if black (which is represented as a true boolean) */ private boolean isBlack(final DataElement dataElement) { return blackColor[dataElement.ordinal()]; } /** * Is this node red? * * @param index the KEY or VALUE int * @return true if non-black */ private boolean isRed(final DataElement dataElement) { return !blackColor[dataElement.ordinal()]; } /** * Make this node black. * * @param index the KEY or VALUE int */ private void setBlack(final DataElement dataElement) { blackColor[dataElement.ordinal()] = true; } /** * Make this node red. * * @param index the KEY or VALUE int */ private void setRed(final DataElement dataElement) { blackColor[dataElement.ordinal()] = false; } /** * Make this node the same color as another * * @param node the node whose color we're adopting * @param index the KEY or VALUE int */ private void copyColor(final Node node, final DataElement dataElement) { blackColor[dataElement.ordinal()] = node.blackColor[dataElement.ordinal()]; } private boolean isLeftChild(final DataElement dataElement) { return parentNode[dataElement.ordinal()] != null && parentNode[dataElement.ordinal()].leftNode[dataElement.ordinal()] == this; } private boolean isRightChild(final DataElement dataElement) { return parentNode[dataElement.ordinal()] != null && parentNode[dataElement.ordinal()].rightNode[dataElement.ordinal()] == this; } //------------------------------------------------------------------- /** * Gets the key. * * @return the key corresponding to this entry. */ public K getKey() { return key; } /** * Gets the value. * * @return the value corresponding to this entry. */ public V getValue() { return value; } /** * Optional operation that is not permitted in this implementation * * @param ignored * @return does not return * @throws UnsupportedOperationException always */ public V setValue(final V ignored) throws UnsupportedOperationException { throw new UnsupportedOperationException("Map.Entry.setValue is not supported"); } /** * Compares the specified object with this entry for equality. * Returns true if the given object is also a map entry and * the two entries represent the same mapping. * * @param obj the object to be compared for equality with this entry. * @return true if the specified object is equal to this entry. */ @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof Map.Entry)) { return false; } final Map.Entry e = (Map.Entry) obj; return getKey().equals(e.getKey()) && getValue().equals(e.getValue()); } /** * @return the hash code value for this map entry. */ @Override public int hashCode() { if (!calculatedHashCode) { hashcodeValue = getKey().hashCode() ^ getValue().hashCode(); calculatedHashCode = true; } return hashcodeValue; } } //----------------------------------------------------------------------- /** * The inverse map implementation. */ class Inverse implements OrderedBidiMap { /** Store the keySet once created. */ private Set inverseKeySet; /** Store the valuesSet once created. */ private Set inverseValuesSet; /** Store the entrySet once created. */ private Set> inverseEntrySet; public int size() { return TreeBidiMap.this.size(); } public boolean isEmpty() { return TreeBidiMap.this.isEmpty(); } public K get(final Object key) { return TreeBidiMap.this.getKey(key); } public V getKey(final Object value) { return TreeBidiMap.this.get(value); } public boolean containsKey(final Object key) { return TreeBidiMap.this.containsValue(key); } public boolean containsValue(final Object value) { return TreeBidiMap.this.containsKey(value); } public V firstKey() { if (TreeBidiMap.this.nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } return leastNode(TreeBidiMap.this.rootNode[VALUE.ordinal()], VALUE).getValue(); } public V lastKey() { if (TreeBidiMap.this.nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } return greatestNode(TreeBidiMap.this.rootNode[VALUE.ordinal()], VALUE).getValue(); } public V nextKey(final V key) { checkKey(key); final Node node = nextGreater(TreeBidiMap.this.lookup(key, VALUE), VALUE); return node == null ? null : node.getValue(); } public V previousKey(final V key) { checkKey(key); final Node node = TreeBidiMap.this.nextSmaller(TreeBidiMap.this.lookup(key, VALUE), VALUE); return node == null ? null : node.getValue(); } public K put(final V key, final K value) { final K result = get(key); TreeBidiMap.this.doPut(value, key); return result; } public void putAll(final Map map) { for (final Map.Entry e : map.entrySet()) { put(e.getKey(), e.getValue()); } } public K remove(final Object key) { return TreeBidiMap.this.removeValue(key); } public V removeValue(final Object value) { return TreeBidiMap.this.remove(value); } public void clear() { TreeBidiMap.this.clear(); } public Set keySet() { if (inverseKeySet == null) { inverseKeySet = new ValueView(VALUE); } return inverseKeySet; } public Set values() { if (inverseValuesSet == null) { inverseValuesSet = new KeyView(VALUE); } return inverseValuesSet; } public Set> entrySet() { if (inverseEntrySet == null) { inverseEntrySet = new InverseEntryView(); } return inverseEntrySet; } public OrderedMapIterator mapIterator() { if (isEmpty()) { return EmptyOrderedMapIterator.emptyOrderedMapIterator(); } return new InverseViewMapIterator(VALUE); } public OrderedBidiMap inverseBidiMap() { return TreeBidiMap.this; } @Override public boolean equals(final Object obj) { return TreeBidiMap.this.doEquals(obj, DataElement.VALUE); } @Override public int hashCode() { return TreeBidiMap.this.doHashCode(DataElement.VALUE); } @Override public String toString() { return TreeBidiMap.this.doToString(DataElement.VALUE); } } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 24400; to = 24026.Please, attach your messages.log to new issue! INFO [org.netbeans.modules.java.hints.providers.code.CodeHintProviderImpl$WorkerImpl]: org.netbeans.modules.java.hints.bugs.SuspiciousToArray.run java.lang.NullPointerException at com.sun.tools.javac.api.JavacTrees.getTypeMirror(JavacTrees.java:721) at org.netbeans.modules.java.hints.bugs.SuspiciousToArray.run(SuspiciousToArray.java:107) Caused: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) [catch] at org.netbeans.modules.java.hints.providers.code.CodeHintProviderImpl$WorkerImpl.createErrors(CodeHintProviderImpl.java:336) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.runHint(HintsInvoker.java:790) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.doComputeHints(HintsInvoker.java:567) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHintsImpl(HintsInvoker.java:294) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:239) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:209) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:199) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:294) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:264) at org.netbeans.api.java.source.JavaSource$MultiTask.run(JavaSource.java:493) at org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask(TaskProcessor.java:593) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:191) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:163) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:206) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:203) at org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager.priorityIO(FileChangedManager.java:176) at org.netbeans.modules.masterfs.providers.ProvidedExtensions.priorityIO(ProvidedExtensions.java:360) at org.netbeans.modules.parsing.impl.Utilities.runPriorityIO(Utilities.java:74) at org.netbeans.modules.parsing.impl.TaskProcessor.runUserTask(TaskProcessor.java:203) at org.netbeans.modules.parsing.api.ParserManager.parse(ParserManager.java:108) at org.netbeans.api.java.source.JavaSource.runUserActionTaskImpl(JavaSource.java:443) at org.netbeans.api.java.source.JavaSource.runUserActionTask(JavaSource.java:414) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getLocalVerifiedSpans(BatchSearch.java:264) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.access$600(BatchSearch.java:99) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$LocalIndexEnquirer.validateResource(BatchSearch.java:574) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getVerifiedSpans(BatchSearch.java:207) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:191) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:134) at org.netbeans.modules.java.hints.spiimpl.refactoring.AbstractApplyHintsRefactoringPlugin.performApplyPattern(AbstractApplyHintsRefactoringPlugin.java:141) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.performApplyPattern(FindDuplicatesRefactoringPlugin.java:121) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.prepare(FindDuplicatesRefactoringPlugin.java:86) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare2(AbstractRefactoring.java:437) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare(AbstractRefactoring.java:421) at org.netbeans.modules.refactoring.api.AbstractRefactoring.prepare(AbstractRefactoring.java:232) at org.netbeans.modules.refactoring.spi.impl.ParametersPanel$Prepare.run(ParametersPanel.java:1026) at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1423) at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2033) SEVERE [org.openide.util.Exceptions] java.lang.NullPointerException at com.sun.tools.javac.api.JavacTrees.getTypeMirror(JavacTrees.java:721) at org.netbeans.modules.java.hints.bugs.SuspiciousToArray.run(SuspiciousToArray.java:107) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) [catch] at org.netbeans.modules.java.hints.providers.code.CodeHintProviderImpl$WorkerImpl.createErrors(CodeHintProviderImpl.java:336) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.runHint(HintsInvoker.java:790) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.doComputeHints(HintsInvoker.java:567) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHintsImpl(HintsInvoker.java:294) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:239) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:209) at org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker.computeHints(HintsInvoker.java:199) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:294) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$3.run(BatchSearch.java:264) at org.netbeans.api.java.source.JavaSource$MultiTask.run(JavaSource.java:493) at org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask(TaskProcessor.java:593) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:191) at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:163) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:206) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:203) at org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager.priorityIO(FileChangedManager.java:176) at org.netbeans.modules.masterfs.providers.ProvidedExtensions.priorityIO(ProvidedExtensions.java:360) at org.netbeans.modules.parsing.impl.Utilities.runPriorityIO(Utilities.java:74) at org.netbeans.modules.parsing.impl.TaskProcessor.runUserTask(TaskProcessor.java:203) at org.netbeans.modules.parsing.api.ParserManager.parse(ParserManager.java:108) at org.netbeans.api.java.source.JavaSource.runUserActionTaskImpl(JavaSource.java:443) at org.netbeans.api.java.source.JavaSource.runUserActionTask(JavaSource.java:414) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getLocalVerifiedSpans(BatchSearch.java:264) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.access$600(BatchSearch.java:99) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch$LocalIndexEnquirer.validateResource(BatchSearch.java:574) at org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.getVerifiedSpans(BatchSearch.java:207) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:191) at org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities.applyFixes(BatchUtilities.java:134) at org.netbeans.modules.java.hints.spiimpl.refactoring.AbstractApplyHintsRefactoringPlugin.performApplyPattern(AbstractApplyHintsRefactoringPlugin.java:141) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.performApplyPattern(FindDuplicatesRefactoringPlugin.java:121) at org.netbeans.modules.java.hints.spiimpl.refactoring.FindDuplicatesRefactoringPlugin.prepare(FindDuplicatesRefactoringPlugin.java:86) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare2(AbstractRefactoring.java:437) at org.netbeans.modules.refactoring.api.AbstractRefactoring.pluginsPrepare(AbstractRefactoring.java:421) at org.netbeans.modules.refactoring.api.AbstractRefactoring.prepare(AbstractRefactoring.java:232) at org.netbeans.modules.refactoring.spi.impl.ParametersPanel$Prepare.run(ParametersPanel.java:1026) at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1423) at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2033) ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Map; import org.apache.commons.collections4.Transformer; /** * Decorates another Map to transform objects that are added. *

* The Map put methods and Map.Entry setValue method are affected by this class. * Thus objects must be removed or searched for using their transformed form. * For example, if the transformation converts Strings to Integers, you must * use the Integer form to remove objects. *

* Note that TransformedMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. The simplest approach is to wrap this map * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw * exceptions when accessed by concurrent threads without synchronization. *

* This class is Serializable from Commons Collections 3.1. *

* @see org.apache.commons.collections4.splitmap.TransformedSplitMap * * @since 3.0 * @version $Id: TransformedMap.java 1491944 2013-06-11 20:29:22Z tn $ */ public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable { /** Serialization version */ private static final long serialVersionUID = 7023152376788900464L; /** The transformer to use for the key */ protected final Transformer keyTransformer; /** The transformer to use for the value */ protected final Transformer valueTransformer; /** * Factory method to create a transforming map. *

* If there are any elements already in the map being decorated, they * are NOT transformed. * Contrast this with {@link #transformedMap(Map, Transformer, Transformer)}. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null means no transformation * @param valueTransformer the transformer to use for value conversion, null means no transformation * @return a new transformed map * @throws IllegalArgumentException if map is null * @since 4.0 */ public static TransformedMap transformingMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { return new TransformedMap(map, keyTransformer, valueTransformer); } /** * Factory method to create a transforming map that will transform * existing contents of the specified map. *

* If there are any elements already in the map being decorated, they * will be transformed by this method. * Contrast this with {@link #transformingMap(Map, Transformer, Transformer)}. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null means no transformation * @param valueTransformer the transformer to use for value conversion, null means no transformation * @return a new transformed map * @throws IllegalArgumentException if map is null * @since 4.0 */ public static TransformedMap transformedMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { final TransformedMap decorated = new TransformedMap(map, keyTransformer, valueTransformer); if (map.size() > 0) { final Map transformed = decorated.transformMap(map); decorated.clear(); decorated.decorated().putAll(transformed); // avoids double transformation } return decorated; } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). *

* If there are any elements already in the collection being decorated, they * are NOT transformed. * * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null means no conversion * @param valueTransformer the transformer to use for value conversion, null means no conversion * @throws IllegalArgumentException if map is null */ protected TransformedMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { super(map); this.keyTransformer = keyTransformer; this.valueTransformer = valueTransformer; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 3.1 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 3.1 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Transforms a key. *

* The transformer itself may throw an exception if necessary. * * @param object the object to transform * @return the transformed object */ protected K transformKey(final K object) { if (keyTransformer == null) { return object; } return keyTransformer.transform(object); } /** * Transforms a value. *

* The transformer itself may throw an exception if necessary. * * @param object the object to transform * @return the transformed object */ protected V transformValue(final V object) { if (valueTransformer == null) { return object; } return valueTransformer.transform(object); } /** * Transforms a map. *

* The transformer itself may throw an exception if necessary. * * @param map the map to transform * @return the transformed object */ @SuppressWarnings("unchecked") protected Map transformMap(final Map map) { if (map.isEmpty()) { return (Map) map; } final Map result = new LinkedMap(map.size()); for (final Map.Entry entry : map.entrySet()) { result.put(transformKey(entry.getKey()), transformValue(entry.getValue())); } return result; } /** * Override to transform the value when using setValue. * * @param value the value to transform * @return the transformed value * @since 3.1 */ @Override protected V checkSetValue(final V value) { return valueTransformer.transform(value); } /** * Override to only return true when there is a value transformer. * * @return true if a value transformer is in use * @since 3.1 */ @Override protected boolean isSetValueChecking() { return valueTransformer != null; } //----------------------------------------------------------------------- @Override public V put(K key, V value) { key = transformKey(key); value = transformValue(value); return decorated().put(key, value); } @Override public void putAll(Map mapToCopy) { mapToCopy = transformMap(mapToCopy); decorated().putAll(mapToCopy); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 6488; to = 6404.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Map; import org.apache.commons.collections4.Transformer; /** * Decorates another Map to transform objects that are added. *

* The Map put methods and Map.Entry setValue method are affected by this class. * Thus objects must be removed or searched for using their transformed form. * For example, if the transformation converts Strings to Integers, you must * use the Integer form to remove objects. *

* Note that TransformedMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. The simplest approach is to wrap this map * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw * exceptions when accessed by concurrent threads without synchronization. *

* This class is Serializable from Commons Collections 3.1. *

* @see org.apache.commons.collections4.splitmap.TransformedSplitMap * * @since 3.0 * @version $Id: TransformedMap.java 1491944 2013-06-11 20:29:22Z tn $ */ public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable { /** Serialization version */ private static final long serialVersionUID = 7023152376788900464L; /** The transformer to use for the key */ protected final Transformer keyTransformer; /** The transformer to use for the value */ protected final Transformer valueTransformer; /** * Factory method to create a transforming map. *

* If there are any elements already in the map being decorated, they * are NOT transformed. * Contrast this with {@link #transformedMap(Map, Transformer, Transformer)}. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null means no transformation * @param valueTransformer the transformer to use for value conversion, null means no transformation * @return a new transformed map * @throws IllegalArgumentException if map is null * @since 4.0 */ public static TransformedMap transformingMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { return new TransformedMap(map, keyTransformer, valueTransformer); } /** * Factory method to create a transforming map that will transform * existing contents of the specified map. *

* If there are any elements already in the map being decorated, they * will be transformed by this method. * Contrast this with {@link #transformingMap(Map, Transformer, Transformer)}. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null means no transformation * @param valueTransformer the transformer to use for value conversion, null means no transformation * @return a new transformed map * @throws IllegalArgumentException if map is null * @since 4.0 */ public static TransformedMap transformedMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { final TransformedMap decorated = new TransformedMap(map, keyTransformer, valueTransformer); if (map.size() > 0) { final Map transformed = decorated.transformMap(map); decorated.clear(); decorated.decorated().putAll(transformed); // avoids double transformation } return decorated; } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). *

* If there are any elements already in the collection being decorated, they * are NOT transformed. * * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null means no conversion * @param valueTransformer the transformer to use for value conversion, null means no conversion * @throws IllegalArgumentException if map is null */ protected TransformedMap(final Map map, final Transformer keyTransformer, final Transformer valueTransformer) { super(map); this.keyTransformer = keyTransformer; this.valueTransformer = valueTransformer; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 3.1 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 3.1 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Transforms a key. *

* The transformer itself may throw an exception if necessary. * * @param object the object to transform * @return the transformed object */ protected K transformKey(final K object) { if (keyTransformer == null) { return object; } return keyTransformer.transform(object); } /** * Transforms a value. *

* The transformer itself may throw an exception if necessary. * * @param object the object to transform * @return the transformed object */ protected V transformValue(final V object) { if (valueTransformer == null) { return object; } return valueTransformer.transform(object); } /** * Transforms a map. *

* The transformer itself may throw an exception if necessary. * * @param map the map to transform * @return the transformed object */ @SuppressWarnings("unchecked") protected Map transformMap(final Map map) { if (map.isEmpty()) { return (Map) map; } final Map result = new LinkedMap(map.size()); for (final Map.Entry entry : map.entrySet()) { result.put(transformKey(entry.getKey()), transformValue(entry.getValue())); } return result; } /** * Override to transform the value when using setValue. * * @param value the value to transform * @return the transformed value * @since 3.1 */ @Override protected V checkSetValue(final V value) { return valueTransformer.transform(value); } /** * Override to only return true when there is a value transformer. * * @return true if a value transformer is in use * @since 3.1 */ @Override protected boolean isSetValueChecking() { return valueTransformer != null; } //----------------------------------------------------------------------- @Override public V put(K key, V value) { key = transformKey(key); value = transformValue(value); return decorated().put(key, value); } @Override public void putAll(Map mapToCopy) { mapToCopy = transformMap(mapToCopy); decorated().putAll(mapToCopy); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 6488; to = 6404.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Factory; import org.apache.commons.collections4.FunctorException; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.iterators.EmptyIterator; import org.apache.commons.collections4.iterators.IteratorChain; import org.apache.commons.collections4.iterators.LazyIteratorChain; import org.apache.commons.collections4.iterators.TransformIterator; /** * A MultiValueMap decorates another map, allowing it to have * more than one value for a key. *

* A MultiMap is a Map with slightly different semantics. * Putting a value into the map will add the value to a Collection at that key. * Getting a value will return a Collection, holding all the values put to that key. *

* This implementation is a decorator, allowing any Map implementation * to be used as the base. *

* In addition, this implementation allows the type of collection used * for the values to be controlled. By default, an ArrayList * is used, however a Class to instantiate may be specified, * or a factory that returns a Collection instance. *

* Note that MultiValueMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. This class may throw exceptions when accessed * by concurrent threads without synchronization. * * @since 3.2 * @version $Id: MultiValueMap.java 1542763 2013-11-17 17:10:33Z tn $ */ public class MultiValueMap extends AbstractMapDecorator implements MultiMap, Serializable { /** Serialization version */ private static final long serialVersionUID = -2214159910087182007L; /** The factory for creating value collections. */ private final Factory> collectionFactory; /** The cached values. */ private transient Collection valuesView; /** * Creates a map which wraps the given map and * maps keys to ArrayLists. * * @param the key type * @param the value type * @param map the map to wrap * @return a new multi-value map * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static MultiValueMap multiValueMap(final Map> map) { return MultiValueMap. multiValueMap((Map) map, ArrayList.class); } /** * Creates a map which decorates the given map and * maps keys to collections of type collectionClass. * * @param the key type * @param the value type * @param the collection class type * @param map the map to wrap * @param collectionClass the type of the collection class * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Class collectionClass) { return new MultiValueMap(map, new ReflectionFactory(collectionClass)); } /** * Creates a map which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory (must return a Collection object). * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Factory collectionFactory) { return new MultiValueMap(map, collectionFactory); } //----------------------------------------------------------------------- /** * Creates a MultiValueMap based on a HashMap and * storing the multiple values in an ArrayList. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public MultiValueMap() { this(new HashMap(), new ReflectionFactory(ArrayList.class)); } /** * Creates a MultiValueMap which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory which must return a Collection instance */ @SuppressWarnings("unchecked") protected > MultiValueMap(final Map map, final Factory collectionFactory) { super((Map) map); if (collectionFactory == null) { throw new IllegalArgumentException("The factory must not be null"); } this.collectionFactory = collectionFactory; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 4.0 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 4.0 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Clear the map. */ @Override public void clear() { // If you believe that you have GC issues here, try uncommenting this code // Set pairs = getMap().entrySet(); // Iterator pairsIterator = pairs.iterator(); // while (pairsIterator.hasNext()) { // Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); // Collection coll = (Collection) keyValuePair.getValue(); // coll.clear(); // } decorated().clear(); } /** * Removes a specific value from map. *

* The item is removed from the collection mapped to the specified key. * Other values attached to that key are unaffected. *

* If the last value for a key is removed, null will be returned * from a subsequent get(key). * * @param key the key to remove from * @param value the value to remove * @return {@code true} if the mapping was removed, {@code false} otherwise */ public boolean removeMapping(final Object key, final Object value) { final Collection valuesForKey = getCollection(key); if (valuesForKey == null) { return false; } final boolean removed = valuesForKey.remove(value); if (removed == false) { return false; } if (valuesForKey.isEmpty()) { remove(key); } return true; } /** * Checks whether the map contains the value specified. *

* This checks all collections against all keys for the value, and thus could be slow. * * @param value the value to search for * @return true if the map contains the value */ @Override @SuppressWarnings("unchecked") public boolean containsValue(final Object value) { final Set> pairs = decorated().entrySet(); if (pairs != null) { for (final Map.Entry entry : pairs) { if (((Collection) entry.getValue()).contains(value)) { return true; } } } return false; } /** * Adds the value to the collection associated with the specified key. *

* Unlike a normal Map the previous value is not replaced. * Instead the new value is added to the collection stored against the key. * * @param key the key to store against * @param value the value to add to the collection at the key * @return the value added if the map changed and null if the map did not change */ @Override @SuppressWarnings("unchecked") public Object put(final K key, final Object value) { boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(1); // might produce a non-empty collection coll.add((V) value); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.add((V) value); } return result ? value : null; } /** * Override superclass to ensure that MultiMap instances are * correctly handled. *

* If you call this method with a normal map, each entry is * added using put(Object,Object). * If you call this method with a multi map, each entry is * added using putAll(Object,Collection). * * @param map the map to copy (either a normal or multi map) */ @Override @SuppressWarnings("unchecked") public void putAll(final Map map) { if (map instanceof MultiMap) { for (final Map.Entry entry : ((MultiMap) map).entrySet()) { putAll(entry.getKey(), (Collection) entry.getValue()); } } else { for (final Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } } /** * {@inheritDoc} *

* NOTE: the returned Entry objects will contain as value a {@link Collection} * of all values that are mapped to the given key. To get a "flattened" version * of all mappings contained in this map, use {@link #iterator()}. * * @see #iterator() */ @Override public Set> entrySet() { return super.entrySet(); } /** * Gets a collection containing all the values in the map. *

* This returns a collection containing the combination of values from all keys. * * @return a collection view of the values contained in this map */ @Override @SuppressWarnings("unchecked") public Collection values() { final Collection vs = valuesView; return (Collection) (vs != null ? vs : (valuesView = new Values())); } /** * Checks whether the collection at the specified key contains the value. * * @param key the key to search for * @param value the value to search for * @return true if the map contains the value */ public boolean containsValue(final Object key, final Object value) { final Collection coll = getCollection(key); if (coll == null) { return false; } return coll.contains(value); } /** * Gets the collection mapped to the specified key. * This method is a convenience method to typecast the result of get(key). * * @param key the key to retrieve * @return the collection mapped to the key, null if no mapping */ @SuppressWarnings("unchecked") public Collection getCollection(final Object key) { return (Collection) decorated().get(key); } /** * Gets the size of the collection mapped to the specified key. * * @param key the key to get size for * @return the size of the collection at the key, zero if key not in map */ public int size(final Object key) { final Collection coll = getCollection(key); if (coll == null) { return 0; } return coll.size(); } /** * Adds a collection of values to the collection associated with * the specified key. * * @param key the key to store against * @param values the values to add to the collection at the key, null ignored * @return true if this map changed */ public boolean putAll(final K key, final Collection values) { if (values == null || values.size() == 0) { return false; } boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(values.size()); // might produce a non-empty collection coll.addAll(values); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.addAll(values); } return result; } /** * Gets an iterator for the collection mapped to the specified key. * * @param key the key to get an iterator for * @return the iterator of the collection at the key, empty iterator if key not in map */ public Iterator iterator(final Object key) { if (!containsKey(key)) { return EmptyIterator.emptyIterator(); } return new ValuesIterator(key); } /** * Gets an iterator for all mappings stored in this {@link MultiValueMap}. *

* The iterator will return multiple Entry objects with the same key * if there are multiple values mapped to this key. *

* NOTE: calling {@link java.util.Map.Entry#setValue(Object)} on any of the returned * elements will result in a {@link UnsupportedOperationException}. * * @return the iterator of all mappings in this map * @since 4.0 */ public Iterator> iterator() { final Collection allKeys = new ArrayList(keySet()); final Iterator keyIterator = allKeys.iterator(); return new LazyIteratorChain>() { @Override protected Iterator> nextIterator(int count) { if ( ! keyIterator.hasNext() ) { return null; } final K key = keyIterator.next(); final Transformer> transformer = new Transformer>() { public Entry transform(final V input) { return new Entry() { public K getKey() { return key; } public V getValue() { return input; } public V setValue(V value) { throw new UnsupportedOperationException(); } }; } }; return new TransformIterator>(new ValuesIterator(key), transformer); } }; } /** * Gets the total size of the map by counting all the values. * * @return the total size of the map counting all values */ public int totalSize() { int total = 0; for (final Object v : decorated().values()) { total += CollectionUtils.size(v); } return total; } /** * Creates a new instance of the map value Collection container * using the factory. *

* This method can be overridden to perform your own processing * instead of using the factory. * * @param size the collection size that is about to be added * @return the new collection */ protected Collection createCollection(final int size) { return collectionFactory.create(); } //----------------------------------------------------------------------- /** * Inner class that provides the values view. */ private class Values extends AbstractCollection { @Override public Iterator iterator() { final IteratorChain chain = new IteratorChain(); for (final K k : keySet()) { chain.addIterator(new ValuesIterator(k)); } return chain; } @Override public int size() { return totalSize(); } @Override public void clear() { MultiValueMap.this.clear(); } } /** * Inner class that provides the values iterator. */ private class ValuesIterator implements Iterator { private final Object key; private final Collection values; private final Iterator iterator; public ValuesIterator(final Object key) { this.key = key; this.values = getCollection(key); this.iterator = values.iterator(); } public void remove() { iterator.remove(); if (values.isEmpty()) { MultiValueMap.this.remove(key); } } public boolean hasNext() { return iterator.hasNext(); } public V next() { return iterator.next(); } } /** * Inner class that provides a simple reflection factory. */ private static class ReflectionFactory> implements Factory, Serializable { /** Serialization version */ private static final long serialVersionUID = 2986114157496788874L; private final Class clazz; public ReflectionFactory(final Class clazz) { this.clazz = clazz; } public T create() { try { return clazz.newInstance(); } catch (final Exception ex) { throw new FunctorException("Cannot instantiate class: " + clazz, ex); } } } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 5085; to = 5066.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Factory; import org.apache.commons.collections4.FunctorException; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.iterators.EmptyIterator; import org.apache.commons.collections4.iterators.IteratorChain; import org.apache.commons.collections4.iterators.LazyIteratorChain; import org.apache.commons.collections4.iterators.TransformIterator; /** * A MultiValueMap decorates another map, allowing it to have * more than one value for a key. *

* A MultiMap is a Map with slightly different semantics. * Putting a value into the map will add the value to a Collection at that key. * Getting a value will return a Collection, holding all the values put to that key. *

* This implementation is a decorator, allowing any Map implementation * to be used as the base. *

* In addition, this implementation allows the type of collection used * for the values to be controlled. By default, an ArrayList * is used, however a Class to instantiate may be specified, * or a factory that returns a Collection instance. *

* Note that MultiValueMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. This class may throw exceptions when accessed * by concurrent threads without synchronization. * * @since 3.2 * @version $Id: MultiValueMap.java 1542763 2013-11-17 17:10:33Z tn $ */ public class MultiValueMap extends AbstractMapDecorator implements MultiMap, Serializable { /** Serialization version */ private static final long serialVersionUID = -2214159910087182007L; /** The factory for creating value collections. */ private final Factory> collectionFactory; /** The cached values. */ private transient Collection valuesView; /** * Creates a map which wraps the given map and * maps keys to ArrayLists. * * @param the key type * @param the value type * @param map the map to wrap * @return a new multi-value map * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static MultiValueMap multiValueMap(final Map> map) { return MultiValueMap. multiValueMap((Map) map, ArrayList.class); } /** * Creates a map which decorates the given map and * maps keys to collections of type collectionClass. * * @param the key type * @param the value type * @param the collection class type * @param map the map to wrap * @param collectionClass the type of the collection class * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Class collectionClass) { return new MultiValueMap(map, new ReflectionFactory(collectionClass)); } /** * Creates a map which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory (must return a Collection object). * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Factory collectionFactory) { return new MultiValueMap(map, collectionFactory); } //----------------------------------------------------------------------- /** * Creates a MultiValueMap based on a HashMap and * storing the multiple values in an ArrayList. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public MultiValueMap() { this(new HashMap(), new ReflectionFactory(ArrayList.class)); } /** * Creates a MultiValueMap which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory which must return a Collection instance */ @SuppressWarnings("unchecked") protected > MultiValueMap(final Map map, final Factory collectionFactory) { super((Map) map); if (collectionFactory == null) { throw new IllegalArgumentException("The factory must not be null"); } this.collectionFactory = collectionFactory; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 4.0 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 4.0 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Clear the map. */ @Override public void clear() { // If you believe that you have GC issues here, try uncommenting this code // Set pairs = getMap().entrySet(); // Iterator pairsIterator = pairs.iterator(); // while (pairsIterator.hasNext()) { // Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); // Collection coll = (Collection) keyValuePair.getValue(); // coll.clear(); // } decorated().clear(); } /** * Removes a specific value from map. *

* The item is removed from the collection mapped to the specified key. * Other values attached to that key are unaffected. *

* If the last value for a key is removed, null will be returned * from a subsequent get(key). * * @param key the key to remove from * @param value the value to remove * @return {@code true} if the mapping was removed, {@code false} otherwise */ public boolean removeMapping(final Object key, final Object value) { final Collection valuesForKey = getCollection(key); if (valuesForKey == null) { return false; } final boolean removed = valuesForKey.remove(value); if (removed == false) { return false; } if (valuesForKey.isEmpty()) { remove(key); } return true; } /** * Checks whether the map contains the value specified. *

* This checks all collections against all keys for the value, and thus could be slow. * * @param value the value to search for * @return true if the map contains the value */ @Override @SuppressWarnings("unchecked") public boolean containsValue(final Object value) { final Set> pairs = decorated().entrySet(); if (pairs != null) { for (final Map.Entry entry : pairs) { if (((Collection) entry.getValue()).contains(value)) { return true; } } } return false; } /** * Adds the value to the collection associated with the specified key. *

* Unlike a normal Map the previous value is not replaced. * Instead the new value is added to the collection stored against the key. * * @param key the key to store against * @param value the value to add to the collection at the key * @return the value added if the map changed and null if the map did not change */ @Override @SuppressWarnings("unchecked") public Object put(final K key, final Object value) { boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(1); // might produce a non-empty collection coll.add((V) value); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.add((V) value); } return result ? value : null; } /** * Override superclass to ensure that MultiMap instances are * correctly handled. *

* If you call this method with a normal map, each entry is * added using put(Object,Object). * If you call this method with a multi map, each entry is * added using putAll(Object,Collection). * * @param map the map to copy (either a normal or multi map) */ @Override @SuppressWarnings("unchecked") public void putAll(final Map map) { if (map instanceof MultiMap) { for (final Map.Entry entry : ((MultiMap) map).entrySet()) { putAll(entry.getKey(), (Collection) entry.getValue()); } } else { for (final Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } } /** * {@inheritDoc} *

* NOTE: the returned Entry objects will contain as value a {@link Collection} * of all values that are mapped to the given key. To get a "flattened" version * of all mappings contained in this map, use {@link #iterator()}. * * @see #iterator() */ @Override public Set> entrySet() { return super.entrySet(); } /** * Gets a collection containing all the values in the map. *

* This returns a collection containing the combination of values from all keys. * * @return a collection view of the values contained in this map */ @Override @SuppressWarnings("unchecked") public Collection values() { final Collection vs = valuesView; return (Collection) (vs != null ? vs : (valuesView = new Values())); } /** * Checks whether the collection at the specified key contains the value. * * @param key the key to search for * @param value the value to search for * @return true if the map contains the value */ public boolean containsValue(final Object key, final Object value) { final Collection coll = getCollection(key); if (coll == null) { return false; } return coll.contains(value); } /** * Gets the collection mapped to the specified key. * This method is a convenience method to typecast the result of get(key). * * @param key the key to retrieve * @return the collection mapped to the key, null if no mapping */ @SuppressWarnings("unchecked") public Collection getCollection(final Object key) { return (Collection) decorated().get(key); } /** * Gets the size of the collection mapped to the specified key. * * @param key the key to get size for * @return the size of the collection at the key, zero if key not in map */ public int size(final Object key) { final Collection coll = getCollection(key); if (coll == null) { return 0; } return coll.size(); } /** * Adds a collection of values to the collection associated with * the specified key. * * @param key the key to store against * @param values the values to add to the collection at the key, null ignored * @return true if this map changed */ public boolean putAll(final K key, final Collection values) { if (values == null || values.size() == 0) { return false; } boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(values.size()); // might produce a non-empty collection coll.addAll(values); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.addAll(values); } return result; } /** * Gets an iterator for the collection mapped to the specified key. * * @param key the key to get an iterator for * @return the iterator of the collection at the key, empty iterator if key not in map */ public Iterator iterator(final Object key) { if (!containsKey(key)) { return EmptyIterator.emptyIterator(); } return new ValuesIterator(key); } /** * Gets an iterator for all mappings stored in this {@link MultiValueMap}. *

* The iterator will return multiple Entry objects with the same key * if there are multiple values mapped to this key. *

* NOTE: calling {@link java.util.Map.Entry#setValue(Object)} on any of the returned * elements will result in a {@link UnsupportedOperationException}. * * @return the iterator of all mappings in this map * @since 4.0 */ public Iterator> iterator() { final Collection allKeys = new ArrayList(keySet()); final Iterator keyIterator = allKeys.iterator(); return new LazyIteratorChain>() { @Override protected Iterator> nextIterator(int count) { if ( ! keyIterator.hasNext() ) { return null; } final K key = keyIterator.next(); final Transformer> transformer = new Transformer>() { public Entry transform(final V input) { return new Entry() { public K getKey() { return key; } public V getValue() { return input; } public V setValue(V value) { throw new UnsupportedOperationException(); } }; } }; return new TransformIterator>(new ValuesIterator(key), transformer); } }; } /** * Gets the total size of the map by counting all the values. * * @return the total size of the map counting all values */ public int totalSize() { int total = 0; for (final Object v : decorated().values()) { total += CollectionUtils.size(v); } return total; } /** * Creates a new instance of the map value Collection container * using the factory. *

* This method can be overridden to perform your own processing * instead of using the factory. * * @param size the collection size that is about to be added * @return the new collection */ protected Collection createCollection(final int size) { return collectionFactory.create(); } //----------------------------------------------------------------------- /** * Inner class that provides the values view. */ private class Values extends AbstractCollection { @Override public Iterator iterator() { final IteratorChain chain = new IteratorChain(); for (final K k : keySet()) { chain.addIterator(new ValuesIterator(k)); } return chain; } @Override public int size() { return totalSize(); } @Override public void clear() { MultiValueMap.this.clear(); } } /** * Inner class that provides the values iterator. */ private class ValuesIterator implements Iterator { private final Object key; private final Collection values; private final Iterator iterator; public ValuesIterator(final Object key) { this.key = key; this.values = getCollection(key); this.iterator = values.iterator(); } public void remove() { iterator.remove(); if (values.isEmpty()) { MultiValueMap.this.remove(key); } } public boolean hasNext() { return iterator.hasNext(); } public V next() { return iterator.next(); } } /** * Inner class that provides a simple reflection factory. */ private static class ReflectionFactory> implements Factory, Serializable { /** Serialization version */ private static final long serialVersionUID = 2986114157496788874L; private final Class clazz; public ReflectionFactory(final Class clazz) { this.clazz = clazz; } public T create() { try { return clazz.newInstance(); } catch (final Exception ex) { throw new FunctorException("Cannot instantiate class: " + clazz, ex); } } } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 6011; to = 6007.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Factory; import org.apache.commons.collections4.FunctorException; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.iterators.EmptyIterator; import org.apache.commons.collections4.iterators.IteratorChain; import org.apache.commons.collections4.iterators.LazyIteratorChain; import org.apache.commons.collections4.iterators.TransformIterator; /** * A MultiValueMap decorates another map, allowing it to have * more than one value for a key. *

* A MultiMap is a Map with slightly different semantics. * Putting a value into the map will add the value to a Collection at that key. * Getting a value will return a Collection, holding all the values put to that key. *

* This implementation is a decorator, allowing any Map implementation * to be used as the base. *

* In addition, this implementation allows the type of collection used * for the values to be controlled. By default, an ArrayList * is used, however a Class to instantiate may be specified, * or a factory that returns a Collection instance. *

* Note that MultiValueMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. This class may throw exceptions when accessed * by concurrent threads without synchronization. * * @since 3.2 * @version $Id: MultiValueMap.java 1542763 2013-11-17 17:10:33Z tn $ */ public class MultiValueMap extends AbstractMapDecorator implements MultiMap, Serializable { /** Serialization version */ private static final long serialVersionUID = -2214159910087182007L; /** The factory for creating value collections. */ private final Factory> collectionFactory; /** The cached values. */ private transient Collection valuesView; /** * Creates a map which wraps the given map and * maps keys to ArrayLists. * * @param the key type * @param the value type * @param map the map to wrap * @return a new multi-value map * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static MultiValueMap multiValueMap(final Map> map) { return MultiValueMap. multiValueMap((Map) map, ArrayList.class); } /** * Creates a map which decorates the given map and * maps keys to collections of type collectionClass. * * @param the key type * @param the value type * @param the collection class type * @param map the map to wrap * @param collectionClass the type of the collection class * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Class collectionClass) { return new MultiValueMap(map, new ReflectionFactory(collectionClass)); } /** * Creates a map which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory (must return a Collection object). * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Factory collectionFactory) { return new MultiValueMap(map, collectionFactory); } //----------------------------------------------------------------------- /** * Creates a MultiValueMap based on a HashMap and * storing the multiple values in an ArrayList. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public MultiValueMap() { this(new HashMap(), new ReflectionFactory(ArrayList.class)); } /** * Creates a MultiValueMap which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory which must return a Collection instance */ @SuppressWarnings("unchecked") protected > MultiValueMap(final Map map, final Factory collectionFactory) { super((Map) map); if (collectionFactory == null) { throw new IllegalArgumentException("The factory must not be null"); } this.collectionFactory = collectionFactory; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 4.0 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 4.0 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Clear the map. */ @Override public void clear() { // If you believe that you have GC issues here, try uncommenting this code // Set pairs = getMap().entrySet(); // Iterator pairsIterator = pairs.iterator(); // while (pairsIterator.hasNext()) { // Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); // Collection coll = (Collection) keyValuePair.getValue(); // coll.clear(); // } decorated().clear(); } /** * Removes a specific value from map. *

* The item is removed from the collection mapped to the specified key. * Other values attached to that key are unaffected. *

* If the last value for a key is removed, null will be returned * from a subsequent get(key). * * @param key the key to remove from * @param value the value to remove * @return {@code true} if the mapping was removed, {@code false} otherwise */ public boolean removeMapping(final Object key, final Object value) { final Collection valuesForKey = getCollection(key); if (valuesForKey == null) { return false; } final boolean removed = valuesForKey.remove(value); if (removed == false) { return false; } if (valuesForKey.isEmpty()) { remove(key); } return true; } /** * Checks whether the map contains the value specified. *

* This checks all collections against all keys for the value, and thus could be slow. * * @param value the value to search for * @return true if the map contains the value */ @Override @SuppressWarnings("unchecked") public boolean containsValue(final Object value) { final Set> pairs = decorated().entrySet(); if (pairs != null) { for (final Map.Entry entry : pairs) { if (((Collection) entry.getValue()).contains(value)) { return true; } } } return false; } /** * Adds the value to the collection associated with the specified key. *

* Unlike a normal Map the previous value is not replaced. * Instead the new value is added to the collection stored against the key. * * @param key the key to store against * @param value the value to add to the collection at the key * @return the value added if the map changed and null if the map did not change */ @Override @SuppressWarnings("unchecked") public Object put(final K key, final Object value) { boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(1); // might produce a non-empty collection coll.add((V) value); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.add((V) value); } return result ? value : null; } /** * Override superclass to ensure that MultiMap instances are * correctly handled. *

* If you call this method with a normal map, each entry is * added using put(Object,Object). * If you call this method with a multi map, each entry is * added using putAll(Object,Collection). * * @param map the map to copy (either a normal or multi map) */ @Override @SuppressWarnings("unchecked") public void putAll(final Map map) { if (map instanceof MultiMap) { for (final Map.Entry entry : ((MultiMap) map).entrySet()) { putAll(entry.getKey(), (Collection) entry.getValue()); } } else { for (final Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } } /** * {@inheritDoc} *

* NOTE: the returned Entry objects will contain as value a {@link Collection} * of all values that are mapped to the given key. To get a "flattened" version * of all mappings contained in this map, use {@link #iterator()}. * * @see #iterator() */ @Override public Set> entrySet() { return super.entrySet(); } /** * Gets a collection containing all the values in the map. *

* This returns a collection containing the combination of values from all keys. * * @return a collection view of the values contained in this map */ @Override @SuppressWarnings("unchecked") public Collection values() { final Collection vs = valuesView; return (Collection) (vs != null ? vs : (valuesView = new Values())); } /** * Checks whether the collection at the specified key contains the value. * * @param key the key to search for * @param value the value to search for * @return true if the map contains the value */ public boolean containsValue(final Object key, final Object value) { final Collection coll = getCollection(key); if (coll == null) { return false; } return coll.contains(value); } /** * Gets the collection mapped to the specified key. * This method is a convenience method to typecast the result of get(key). * * @param key the key to retrieve * @return the collection mapped to the key, null if no mapping */ @SuppressWarnings("unchecked") public Collection getCollection(final Object key) { return (Collection) decorated().get(key); } /** * Gets the size of the collection mapped to the specified key. * * @param key the key to get size for * @return the size of the collection at the key, zero if key not in map */ public int size(final Object key) { final Collection coll = getCollection(key); if (coll == null) { return 0; } return coll.size(); } /** * Adds a collection of values to the collection associated with * the specified key. * * @param key the key to store against * @param values the values to add to the collection at the key, null ignored * @return true if this map changed */ public boolean putAll(final K key, final Collection values) { if (values == null || values.size() == 0) { return false; } boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(values.size()); // might produce a non-empty collection coll.addAll(values); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.addAll(values); } return result; } /** * Gets an iterator for the collection mapped to the specified key. * * @param key the key to get an iterator for * @return the iterator of the collection at the key, empty iterator if key not in map */ public Iterator iterator(final Object key) { if (!containsKey(key)) { return EmptyIterator.emptyIterator(); } return new ValuesIterator(key); } /** * Gets an iterator for all mappings stored in this {@link MultiValueMap}. *

* The iterator will return multiple Entry objects with the same key * if there are multiple values mapped to this key. *

* NOTE: calling {@link java.util.Map.Entry#setValue(Object)} on any of the returned * elements will result in a {@link UnsupportedOperationException}. * * @return the iterator of all mappings in this map * @since 4.0 */ public Iterator> iterator() { final Collection allKeys = new ArrayList(keySet()); final Iterator keyIterator = allKeys.iterator(); return new LazyIteratorChain>() { @Override protected Iterator> nextIterator(int count) { if ( ! keyIterator.hasNext() ) { return null; } final K key = keyIterator.next(); final Transformer> transformer = new Transformer>() { public Entry transform(final V input) { return new Entry() { public K getKey() { return key; } public V getValue() { return input; } public V setValue(V value) { throw new UnsupportedOperationException(); } }; } }; return new TransformIterator>(new ValuesIterator(key), transformer); } }; } /** * Gets the total size of the map by counting all the values. * * @return the total size of the map counting all values */ public int totalSize() { int total = 0; for (final Object v : decorated().values()) { total += CollectionUtils.size(v); } return total; } /** * Creates a new instance of the map value Collection container * using the factory. *

* This method can be overridden to perform your own processing * instead of using the factory. * * @param size the collection size that is about to be added * @return the new collection */ protected Collection createCollection(final int size) { return collectionFactory.create(); } //----------------------------------------------------------------------- /** * Inner class that provides the values view. */ private class Values extends AbstractCollection { @Override public Iterator iterator() { final IteratorChain chain = new IteratorChain(); for (final K k : keySet()) { chain.addIterator(new ValuesIterator(k)); } return chain; } @Override public int size() { return totalSize(); } @Override public void clear() { MultiValueMap.this.clear(); } } /** * Inner class that provides the values iterator. */ private class ValuesIterator implements Iterator { private final Object key; private final Collection values; private final Iterator iterator; public ValuesIterator(final Object key) { this.key = key; this.values = getCollection(key); this.iterator = values.iterator(); } public void remove() { iterator.remove(); if (values.isEmpty()) { MultiValueMap.this.remove(key); } } public boolean hasNext() { return iterator.hasNext(); } public V next() { return iterator.next(); } } /** * Inner class that provides a simple reflection factory. */ private static class ReflectionFactory> implements Factory, Serializable { /** Serialization version */ private static final long serialVersionUID = 2986114157496788874L; private final Class clazz; public ReflectionFactory(final Class clazz) { this.clazz = clazz; } public T create() { try { return clazz.newInstance(); } catch (final Exception ex) { throw new FunctorException("Cannot instantiate class: " + clazz, ex); } } } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 6119; to = 6082.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Factory; import org.apache.commons.collections4.FunctorException; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.iterators.EmptyIterator; import org.apache.commons.collections4.iterators.IteratorChain; import org.apache.commons.collections4.iterators.LazyIteratorChain; import org.apache.commons.collections4.iterators.TransformIterator; /** * A MultiValueMap decorates another map, allowing it to have * more than one value for a key. *

* A MultiMap is a Map with slightly different semantics. * Putting a value into the map will add the value to a Collection at that key. * Getting a value will return a Collection, holding all the values put to that key. *

* This implementation is a decorator, allowing any Map implementation * to be used as the base. *

* In addition, this implementation allows the type of collection used * for the values to be controlled. By default, an ArrayList * is used, however a Class to instantiate may be specified, * or a factory that returns a Collection instance. *

* Note that MultiValueMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. This class may throw exceptions when accessed * by concurrent threads without synchronization. * * @since 3.2 * @version $Id: MultiValueMap.java 1542763 2013-11-17 17:10:33Z tn $ */ public class MultiValueMap extends AbstractMapDecorator implements MultiMap, Serializable { /** Serialization version */ private static final long serialVersionUID = -2214159910087182007L; /** The factory for creating value collections. */ private final Factory> collectionFactory; /** The cached values. */ private transient Collection valuesView; /** * Creates a map which wraps the given map and * maps keys to ArrayLists. * * @param the key type * @param the value type * @param map the map to wrap * @return a new multi-value map * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static MultiValueMap multiValueMap(final Map> map) { return MultiValueMap. multiValueMap((Map) map, ArrayList.class); } /** * Creates a map which decorates the given map and * maps keys to collections of type collectionClass. * * @param the key type * @param the value type * @param the collection class type * @param map the map to wrap * @param collectionClass the type of the collection class * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Class collectionClass) { return new MultiValueMap(map, new ReflectionFactory(collectionClass)); } /** * Creates a map which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory (must return a Collection object). * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Factory collectionFactory) { return new MultiValueMap(map, collectionFactory); } //----------------------------------------------------------------------- /** * Creates a MultiValueMap based on a HashMap and * storing the multiple values in an ArrayList. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public MultiValueMap() { this(new HashMap(), new ReflectionFactory(ArrayList.class)); } /** * Creates a MultiValueMap which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory which must return a Collection instance */ @SuppressWarnings("unchecked") protected > MultiValueMap(final Map map, final Factory collectionFactory) { super((Map) map); if (collectionFactory == null) { throw new IllegalArgumentException("The factory must not be null"); } this.collectionFactory = collectionFactory; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 4.0 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 4.0 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Clear the map. */ @Override public void clear() { // If you believe that you have GC issues here, try uncommenting this code // Set pairs = getMap().entrySet(); // Iterator pairsIterator = pairs.iterator(); // while (pairsIterator.hasNext()) { // Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); // Collection coll = (Collection) keyValuePair.getValue(); // coll.clear(); // } decorated().clear(); } /** * Removes a specific value from map. *

* The item is removed from the collection mapped to the specified key. * Other values attached to that key are unaffected. *

* If the last value for a key is removed, null will be returned * from a subsequent get(key). * * @param key the key to remove from * @param value the value to remove * @return {@code true} if the mapping was removed, {@code false} otherwise */ public boolean removeMapping(final Object key, final Object value) { final Collection valuesForKey = getCollection(key); if (valuesForKey == null) { return false; } final boolean removed = valuesForKey.remove(value); if (removed == false) { return false; } if (valuesForKey.isEmpty()) { remove(key); } return true; } /** * Checks whether the map contains the value specified. *

* This checks all collections against all keys for the value, and thus could be slow. * * @param value the value to search for * @return true if the map contains the value */ @Override @SuppressWarnings("unchecked") public boolean containsValue(final Object value) { final Set> pairs = decorated().entrySet(); if (pairs != null) { for (final Map.Entry entry : pairs) { if (((Collection) entry.getValue()).contains(value)) { return true; } } } return false; } /** * Adds the value to the collection associated with the specified key. *

* Unlike a normal Map the previous value is not replaced. * Instead the new value is added to the collection stored against the key. * * @param key the key to store against * @param value the value to add to the collection at the key * @return the value added if the map changed and null if the map did not change */ @Override @SuppressWarnings("unchecked") public Object put(final K key, final Object value) { boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(1); // might produce a non-empty collection coll.add((V) value); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.add((V) value); } return result ? value : null; } /** * Override superclass to ensure that MultiMap instances are * correctly handled. *

* If you call this method with a normal map, each entry is * added using put(Object,Object). * If you call this method with a multi map, each entry is * added using putAll(Object,Collection). * * @param map the map to copy (either a normal or multi map) */ @Override @SuppressWarnings("unchecked") public void putAll(final Map map) { if (map instanceof MultiMap) { for (final Map.Entry entry : ((MultiMap) map).entrySet()) { putAll(entry.getKey(), (Collection) entry.getValue()); } } else { for (final Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } } /** * {@inheritDoc} *

* NOTE: the returned Entry objects will contain as value a {@link Collection} * of all values that are mapped to the given key. To get a "flattened" version * of all mappings contained in this map, use {@link #iterator()}. * * @see #iterator() */ @Override public Set> entrySet() { return super.entrySet(); } /** * Gets a collection containing all the values in the map. *

* This returns a collection containing the combination of values from all keys. * * @return a collection view of the values contained in this map */ @Override @SuppressWarnings("unchecked") public Collection values() { final Collection vs = valuesView; return (Collection) (vs != null ? vs : (valuesView = new Values())); } /** * Checks whether the collection at the specified key contains the value. * * @param key the key to search for * @param value the value to search for * @return true if the map contains the value */ public boolean containsValue(final Object key, final Object value) { final Collection coll = getCollection(key); if (coll == null) { return false; } return coll.contains(value); } /** * Gets the collection mapped to the specified key. * This method is a convenience method to typecast the result of get(key). * * @param key the key to retrieve * @return the collection mapped to the key, null if no mapping */ @SuppressWarnings("unchecked") public Collection getCollection(final Object key) { return (Collection) decorated().get(key); } /** * Gets the size of the collection mapped to the specified key. * * @param key the key to get size for * @return the size of the collection at the key, zero if key not in map */ public int size(final Object key) { final Collection coll = getCollection(key); if (coll == null) { return 0; } return coll.size(); } /** * Adds a collection of values to the collection associated with * the specified key. * * @param key the key to store against * @param values the values to add to the collection at the key, null ignored * @return true if this map changed */ public boolean putAll(final K key, final Collection values) { if (values == null || values.size() == 0) { return false; } boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(values.size()); // might produce a non-empty collection coll.addAll(values); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.addAll(values); } return result; } /** * Gets an iterator for the collection mapped to the specified key. * * @param key the key to get an iterator for * @return the iterator of the collection at the key, empty iterator if key not in map */ public Iterator iterator(final Object key) { if (!containsKey(key)) { return EmptyIterator.emptyIterator(); } return new ValuesIterator(key); } /** * Gets an iterator for all mappings stored in this {@link MultiValueMap}. *

* The iterator will return multiple Entry objects with the same key * if there are multiple values mapped to this key. *

* NOTE: calling {@link java.util.Map.Entry#setValue(Object)} on any of the returned * elements will result in a {@link UnsupportedOperationException}. * * @return the iterator of all mappings in this map * @since 4.0 */ public Iterator> iterator() { final Collection allKeys = new ArrayList(keySet()); final Iterator keyIterator = allKeys.iterator(); return new LazyIteratorChain>() { @Override protected Iterator> nextIterator(int count) { if ( ! keyIterator.hasNext() ) { return null; } final K key = keyIterator.next(); final Transformer> transformer = new Transformer>() { public Entry transform(final V input) { return new Entry() { public K getKey() { return key; } public V getValue() { return input; } public V setValue(V value) { throw new UnsupportedOperationException(); } }; } }; return new TransformIterator>(new ValuesIterator(key), transformer); } }; } /** * Gets the total size of the map by counting all the values. * * @return the total size of the map counting all values */ public int totalSize() { int total = 0; for (final Object v : decorated().values()) { total += CollectionUtils.size(v); } return total; } /** * Creates a new instance of the map value Collection container * using the factory. *

* This method can be overridden to perform your own processing * instead of using the factory. * * @param size the collection size that is about to be added * @return the new collection */ protected Collection createCollection(final int size) { return collectionFactory.create(); } //----------------------------------------------------------------------- /** * Inner class that provides the values view. */ private class Values extends AbstractCollection { @Override public Iterator iterator() { final IteratorChain chain = new IteratorChain(); for (final K k : keySet()) { chain.addIterator(new ValuesIterator(k)); } return chain; } @Override public int size() { return totalSize(); } @Override public void clear() { MultiValueMap.this.clear(); } } /** * Inner class that provides the values iterator. */ private class ValuesIterator implements Iterator { private final Object key; private final Collection values; private final Iterator iterator; public ValuesIterator(final Object key) { this.key = key; this.values = getCollection(key); this.iterator = values.iterator(); } public void remove() { iterator.remove(); if (values.isEmpty()) { MultiValueMap.this.remove(key); } } public boolean hasNext() { return iterator.hasNext(); } public V next() { return iterator.next(); } } /** * Inner class that provides a simple reflection factory. */ private static class ReflectionFactory> implements Factory, Serializable { /** Serialization version */ private static final long serialVersionUID = 2986114157496788874L; private final Class clazz; public ReflectionFactory(final Class clazz) { this.clazz = clazz; } public T create() { try { return clazz.newInstance(); } catch (final Exception ex) { throw new FunctorException("Cannot instantiate class: " + clazz, ex); } } } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 7103; to = 7015.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Factory; import org.apache.commons.collections4.FunctorException; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.iterators.EmptyIterator; import org.apache.commons.collections4.iterators.IteratorChain; import org.apache.commons.collections4.iterators.LazyIteratorChain; import org.apache.commons.collections4.iterators.TransformIterator; /** * A MultiValueMap decorates another map, allowing it to have * more than one value for a key. *

* A MultiMap is a Map with slightly different semantics. * Putting a value into the map will add the value to a Collection at that key. * Getting a value will return a Collection, holding all the values put to that key. *

* This implementation is a decorator, allowing any Map implementation * to be used as the base. *

* In addition, this implementation allows the type of collection used * for the values to be controlled. By default, an ArrayList * is used, however a Class to instantiate may be specified, * or a factory that returns a Collection instance. *

* Note that MultiValueMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. This class may throw exceptions when accessed * by concurrent threads without synchronization. * * @since 3.2 * @version $Id: MultiValueMap.java 1542763 2013-11-17 17:10:33Z tn $ */ public class MultiValueMap extends AbstractMapDecorator implements MultiMap, Serializable { /** Serialization version */ private static final long serialVersionUID = -2214159910087182007L; /** The factory for creating value collections. */ private final Factory> collectionFactory; /** The cached values. */ private transient Collection valuesView; /** * Creates a map which wraps the given map and * maps keys to ArrayLists. * * @param the key type * @param the value type * @param map the map to wrap * @return a new multi-value map * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static MultiValueMap multiValueMap(final Map> map) { return MultiValueMap. multiValueMap((Map) map, ArrayList.class); } /** * Creates a map which decorates the given map and * maps keys to collections of type collectionClass. * * @param the key type * @param the value type * @param the collection class type * @param map the map to wrap * @param collectionClass the type of the collection class * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Class collectionClass) { return new MultiValueMap(map, new ReflectionFactory(collectionClass)); } /** * Creates a map which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory (must return a Collection object). * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Factory collectionFactory) { return new MultiValueMap(map, collectionFactory); } //----------------------------------------------------------------------- /** * Creates a MultiValueMap based on a HashMap and * storing the multiple values in an ArrayList. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public MultiValueMap() { this(new HashMap(), new ReflectionFactory(ArrayList.class)); } /** * Creates a MultiValueMap which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory which must return a Collection instance */ @SuppressWarnings("unchecked") protected > MultiValueMap(final Map map, final Factory collectionFactory) { super((Map) map); if (collectionFactory == null) { throw new IllegalArgumentException("The factory must not be null"); } this.collectionFactory = collectionFactory; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 4.0 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 4.0 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Clear the map. */ @Override public void clear() { // If you believe that you have GC issues here, try uncommenting this code // Set pairs = getMap().entrySet(); // Iterator pairsIterator = pairs.iterator(); // while (pairsIterator.hasNext()) { // Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); // Collection coll = (Collection) keyValuePair.getValue(); // coll.clear(); // } decorated().clear(); } /** * Removes a specific value from map. *

* The item is removed from the collection mapped to the specified key. * Other values attached to that key are unaffected. *

* If the last value for a key is removed, null will be returned * from a subsequent get(key). * * @param key the key to remove from * @param value the value to remove * @return {@code true} if the mapping was removed, {@code false} otherwise */ public boolean removeMapping(final Object key, final Object value) { final Collection valuesForKey = getCollection(key); if (valuesForKey == null) { return false; } final boolean removed = valuesForKey.remove(value); if (removed == false) { return false; } if (valuesForKey.isEmpty()) { remove(key); } return true; } /** * Checks whether the map contains the value specified. *

* This checks all collections against all keys for the value, and thus could be slow. * * @param value the value to search for * @return true if the map contains the value */ @Override @SuppressWarnings("unchecked") public boolean containsValue(final Object value) { final Set> pairs = decorated().entrySet(); if (pairs != null) { for (final Map.Entry entry : pairs) { if (((Collection) entry.getValue()).contains(value)) { return true; } } } return false; } /** * Adds the value to the collection associated with the specified key. *

* Unlike a normal Map the previous value is not replaced. * Instead the new value is added to the collection stored against the key. * * @param key the key to store against * @param value the value to add to the collection at the key * @return the value added if the map changed and null if the map did not change */ @Override @SuppressWarnings("unchecked") public Object put(final K key, final Object value) { boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(1); // might produce a non-empty collection coll.add((V) value); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.add((V) value); } return result ? value : null; } /** * Override superclass to ensure that MultiMap instances are * correctly handled. *

* If you call this method with a normal map, each entry is * added using put(Object,Object). * If you call this method with a multi map, each entry is * added using putAll(Object,Collection). * * @param map the map to copy (either a normal or multi map) */ @Override @SuppressWarnings("unchecked") public void putAll(final Map map) { if (map instanceof MultiMap) { for (final Map.Entry entry : ((MultiMap) map).entrySet()) { putAll(entry.getKey(), (Collection) entry.getValue()); } } else { for (final Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } } /** * {@inheritDoc} *

* NOTE: the returned Entry objects will contain as value a {@link Collection} * of all values that are mapped to the given key. To get a "flattened" version * of all mappings contained in this map, use {@link #iterator()}. * * @see #iterator() */ @Override public Set> entrySet() { return super.entrySet(); } /** * Gets a collection containing all the values in the map. *

* This returns a collection containing the combination of values from all keys. * * @return a collection view of the values contained in this map */ @Override @SuppressWarnings("unchecked") public Collection values() { final Collection vs = valuesView; return (Collection) (vs != null ? vs : (valuesView = new Values())); } /** * Checks whether the collection at the specified key contains the value. * * @param key the key to search for * @param value the value to search for * @return true if the map contains the value */ public boolean containsValue(final Object key, final Object value) { final Collection coll = getCollection(key); if (coll == null) { return false; } return coll.contains(value); } /** * Gets the collection mapped to the specified key. * This method is a convenience method to typecast the result of get(key). * * @param key the key to retrieve * @return the collection mapped to the key, null if no mapping */ @SuppressWarnings("unchecked") public Collection getCollection(final Object key) { return (Collection) decorated().get(key); } /** * Gets the size of the collection mapped to the specified key. * * @param key the key to get size for * @return the size of the collection at the key, zero if key not in map */ public int size(final Object key) { final Collection coll = getCollection(key); if (coll == null) { return 0; } return coll.size(); } /** * Adds a collection of values to the collection associated with * the specified key. * * @param key the key to store against * @param values the values to add to the collection at the key, null ignored * @return true if this map changed */ public boolean putAll(final K key, final Collection values) { if (values == null || values.size() == 0) { return false; } boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(values.size()); // might produce a non-empty collection coll.addAll(values); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.addAll(values); } return result; } /** * Gets an iterator for the collection mapped to the specified key. * * @param key the key to get an iterator for * @return the iterator of the collection at the key, empty iterator if key not in map */ public Iterator iterator(final Object key) { if (!containsKey(key)) { return EmptyIterator.emptyIterator(); } return new ValuesIterator(key); } /** * Gets an iterator for all mappings stored in this {@link MultiValueMap}. *

* The iterator will return multiple Entry objects with the same key * if there are multiple values mapped to this key. *

* NOTE: calling {@link java.util.Map.Entry#setValue(Object)} on any of the returned * elements will result in a {@link UnsupportedOperationException}. * * @return the iterator of all mappings in this map * @since 4.0 */ public Iterator> iterator() { final Collection allKeys = new ArrayList(keySet()); final Iterator keyIterator = allKeys.iterator(); return new LazyIteratorChain>() { @Override protected Iterator> nextIterator(int count) { if ( ! keyIterator.hasNext() ) { return null; } final K key = keyIterator.next(); final Transformer> transformer = new Transformer>() { public Entry transform(final V input) { return new Entry() { public K getKey() { return key; } public V getValue() { return input; } public V setValue(V value) { throw new UnsupportedOperationException(); } }; } }; return new TransformIterator>(new ValuesIterator(key), transformer); } }; } /** * Gets the total size of the map by counting all the values. * * @return the total size of the map counting all values */ public int totalSize() { int total = 0; for (final Object v : decorated().values()) { total += CollectionUtils.size(v); } return total; } /** * Creates a new instance of the map value Collection container * using the factory. *

* This method can be overridden to perform your own processing * instead of using the factory. * * @param size the collection size that is about to be added * @return the new collection */ protected Collection createCollection(final int size) { return collectionFactory.create(); } //----------------------------------------------------------------------- /** * Inner class that provides the values view. */ private class Values extends AbstractCollection { @Override public Iterator iterator() { final IteratorChain chain = new IteratorChain(); for (final K k : keySet()) { chain.addIterator(new ValuesIterator(k)); } return chain; } @Override public int size() { return totalSize(); } @Override public void clear() { MultiValueMap.this.clear(); } } /** * Inner class that provides the values iterator. */ private class ValuesIterator implements Iterator { private final Object key; private final Collection values; private final Iterator iterator; public ValuesIterator(final Object key) { this.key = key; this.values = getCollection(key); this.iterator = values.iterator(); } public void remove() { iterator.remove(); if (values.isEmpty()) { MultiValueMap.this.remove(key); } } public boolean hasNext() { return iterator.hasNext(); } public V next() { return iterator.next(); } } /** * Inner class that provides a simple reflection factory. */ private static class ReflectionFactory> implements Factory, Serializable { /** Serialization version */ private static final long serialVersionUID = 2986114157496788874L; private final Class clazz; public ReflectionFactory(final Class clazz) { this.clazz = clazz; } public T create() { try { return clazz.newInstance(); } catch (final Exception ex) { throw new FunctorException("Cannot instantiate class: " + clazz, ex); } } } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 5085; to = 5066.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Factory; import org.apache.commons.collections4.FunctorException; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.iterators.EmptyIterator; import org.apache.commons.collections4.iterators.IteratorChain; import org.apache.commons.collections4.iterators.LazyIteratorChain; import org.apache.commons.collections4.iterators.TransformIterator; /** * A MultiValueMap decorates another map, allowing it to have * more than one value for a key. *

* A MultiMap is a Map with slightly different semantics. * Putting a value into the map will add the value to a Collection at that key. * Getting a value will return a Collection, holding all the values put to that key. *

* This implementation is a decorator, allowing any Map implementation * to be used as the base. *

* In addition, this implementation allows the type of collection used * for the values to be controlled. By default, an ArrayList * is used, however a Class to instantiate may be specified, * or a factory that returns a Collection instance. *

* Note that MultiValueMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. This class may throw exceptions when accessed * by concurrent threads without synchronization. * * @since 3.2 * @version $Id: MultiValueMap.java 1542763 2013-11-17 17:10:33Z tn $ */ public class MultiValueMap extends AbstractMapDecorator implements MultiMap, Serializable { /** Serialization version */ private static final long serialVersionUID = -2214159910087182007L; /** The factory for creating value collections. */ private final Factory> collectionFactory; /** The cached values. */ private transient Collection valuesView; /** * Creates a map which wraps the given map and * maps keys to ArrayLists. * * @param the key type * @param the value type * @param map the map to wrap * @return a new multi-value map * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static MultiValueMap multiValueMap(final Map> map) { return MultiValueMap. multiValueMap((Map) map, ArrayList.class); } /** * Creates a map which decorates the given map and * maps keys to collections of type collectionClass. * * @param the key type * @param the value type * @param the collection class type * @param map the map to wrap * @param collectionClass the type of the collection class * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Class collectionClass) { return new MultiValueMap(map, new ReflectionFactory(collectionClass)); } /** * Creates a map which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory (must return a Collection object). * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Factory collectionFactory) { return new MultiValueMap(map, collectionFactory); } //----------------------------------------------------------------------- /** * Creates a MultiValueMap based on a HashMap and * storing the multiple values in an ArrayList. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public MultiValueMap() { this(new HashMap(), new ReflectionFactory(ArrayList.class)); } /** * Creates a MultiValueMap which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory which must return a Collection instance */ @SuppressWarnings("unchecked") protected > MultiValueMap(final Map map, final Factory collectionFactory) { super((Map) map); if (collectionFactory == null) { throw new IllegalArgumentException("The factory must not be null"); } this.collectionFactory = collectionFactory; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 4.0 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 4.0 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Clear the map. */ @Override public void clear() { // If you believe that you have GC issues here, try uncommenting this code // Set pairs = getMap().entrySet(); // Iterator pairsIterator = pairs.iterator(); // while (pairsIterator.hasNext()) { // Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); // Collection coll = (Collection) keyValuePair.getValue(); // coll.clear(); // } decorated().clear(); } /** * Removes a specific value from map. *

* The item is removed from the collection mapped to the specified key. * Other values attached to that key are unaffected. *

* If the last value for a key is removed, null will be returned * from a subsequent get(key). * * @param key the key to remove from * @param value the value to remove * @return {@code true} if the mapping was removed, {@code false} otherwise */ public boolean removeMapping(final Object key, final Object value) { final Collection valuesForKey = getCollection(key); if (valuesForKey == null) { return false; } final boolean removed = valuesForKey.remove(value); if (removed == false) { return false; } if (valuesForKey.isEmpty()) { remove(key); } return true; } /** * Checks whether the map contains the value specified. *

* This checks all collections against all keys for the value, and thus could be slow. * * @param value the value to search for * @return true if the map contains the value */ @Override @SuppressWarnings("unchecked") public boolean containsValue(final Object value) { final Set> pairs = decorated().entrySet(); if (pairs != null) { for (final Map.Entry entry : pairs) { if (((Collection) entry.getValue()).contains(value)) { return true; } } } return false; } /** * Adds the value to the collection associated with the specified key. *

* Unlike a normal Map the previous value is not replaced. * Instead the new value is added to the collection stored against the key. * * @param key the key to store against * @param value the value to add to the collection at the key * @return the value added if the map changed and null if the map did not change */ @Override @SuppressWarnings("unchecked") public Object put(final K key, final Object value) { boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(1); // might produce a non-empty collection coll.add((V) value); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.add((V) value); } return result ? value : null; } /** * Override superclass to ensure that MultiMap instances are * correctly handled. *

* If you call this method with a normal map, each entry is * added using put(Object,Object). * If you call this method with a multi map, each entry is * added using putAll(Object,Collection). * * @param map the map to copy (either a normal or multi map) */ @Override @SuppressWarnings("unchecked") public void putAll(final Map map) { if (map instanceof MultiMap) { for (final Map.Entry entry : ((MultiMap) map).entrySet()) { putAll(entry.getKey(), (Collection) entry.getValue()); } } else { for (final Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } } /** * {@inheritDoc} *

* NOTE: the returned Entry objects will contain as value a {@link Collection} * of all values that are mapped to the given key. To get a "flattened" version * of all mappings contained in this map, use {@link #iterator()}. * * @see #iterator() */ @Override public Set> entrySet() { return super.entrySet(); } /** * Gets a collection containing all the values in the map. *

* This returns a collection containing the combination of values from all keys. * * @return a collection view of the values contained in this map */ @Override @SuppressWarnings("unchecked") public Collection values() { final Collection vs = valuesView; return (Collection) (vs != null ? vs : (valuesView = new Values())); } /** * Checks whether the collection at the specified key contains the value. * * @param key the key to search for * @param value the value to search for * @return true if the map contains the value */ public boolean containsValue(final Object key, final Object value) { final Collection coll = getCollection(key); if (coll == null) { return false; } return coll.contains(value); } /** * Gets the collection mapped to the specified key. * This method is a convenience method to typecast the result of get(key). * * @param key the key to retrieve * @return the collection mapped to the key, null if no mapping */ @SuppressWarnings("unchecked") public Collection getCollection(final Object key) { return (Collection) decorated().get(key); } /** * Gets the size of the collection mapped to the specified key. * * @param key the key to get size for * @return the size of the collection at the key, zero if key not in map */ public int size(final Object key) { final Collection coll = getCollection(key); if (coll == null) { return 0; } return coll.size(); } /** * Adds a collection of values to the collection associated with * the specified key. * * @param key the key to store against * @param values the values to add to the collection at the key, null ignored * @return true if this map changed */ public boolean putAll(final K key, final Collection values) { if (values == null || values.size() == 0) { return false; } boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(values.size()); // might produce a non-empty collection coll.addAll(values); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.addAll(values); } return result; } /** * Gets an iterator for the collection mapped to the specified key. * * @param key the key to get an iterator for * @return the iterator of the collection at the key, empty iterator if key not in map */ public Iterator iterator(final Object key) { if (!containsKey(key)) { return EmptyIterator.emptyIterator(); } return new ValuesIterator(key); } /** * Gets an iterator for all mappings stored in this {@link MultiValueMap}. *

* The iterator will return multiple Entry objects with the same key * if there are multiple values mapped to this key. *

* NOTE: calling {@link java.util.Map.Entry#setValue(Object)} on any of the returned * elements will result in a {@link UnsupportedOperationException}. * * @return the iterator of all mappings in this map * @since 4.0 */ public Iterator> iterator() { final Collection allKeys = new ArrayList(keySet()); final Iterator keyIterator = allKeys.iterator(); return new LazyIteratorChain>() { @Override protected Iterator> nextIterator(int count) { if ( ! keyIterator.hasNext() ) { return null; } final K key = keyIterator.next(); final Transformer> transformer = new Transformer>() { public Entry transform(final V input) { return new Entry() { public K getKey() { return key; } public V getValue() { return input; } public V setValue(V value) { throw new UnsupportedOperationException(); } }; } }; return new TransformIterator>(new ValuesIterator(key), transformer); } }; } /** * Gets the total size of the map by counting all the values. * * @return the total size of the map counting all values */ public int totalSize() { int total = 0; for (final Object v : decorated().values()) { total += CollectionUtils.size(v); } return total; } /** * Creates a new instance of the map value Collection container * using the factory. *

* This method can be overridden to perform your own processing * instead of using the factory. * * @param size the collection size that is about to be added * @return the new collection */ protected Collection createCollection(final int size) { return collectionFactory.create(); } //----------------------------------------------------------------------- /** * Inner class that provides the values view. */ private class Values extends AbstractCollection { @Override public Iterator iterator() { final IteratorChain chain = new IteratorChain(); for (final K k : keySet()) { chain.addIterator(new ValuesIterator(k)); } return chain; } @Override public int size() { return totalSize(); } @Override public void clear() { MultiValueMap.this.clear(); } } /** * Inner class that provides the values iterator. */ private class ValuesIterator implements Iterator { private final Object key; private final Collection values; private final Iterator iterator; public ValuesIterator(final Object key) { this.key = key; this.values = getCollection(key); this.iterator = values.iterator(); } public void remove() { iterator.remove(); if (values.isEmpty()) { MultiValueMap.this.remove(key); } } public boolean hasNext() { return iterator.hasNext(); } public V next() { return iterator.next(); } } /** * Inner class that provides a simple reflection factory. */ private static class ReflectionFactory> implements Factory, Serializable { /** Serialization version */ private static final long serialVersionUID = 2986114157496788874L; private final Class clazz; public ReflectionFactory(final Class clazz) { this.clazz = clazz; } public T create() { try { return clazz.newInstance(); } catch (final Exception ex) { throw new FunctorException("Cannot instantiate class: " + clazz, ex); } } } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 6011; to = 6007.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Factory; import org.apache.commons.collections4.FunctorException; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.iterators.EmptyIterator; import org.apache.commons.collections4.iterators.IteratorChain; import org.apache.commons.collections4.iterators.LazyIteratorChain; import org.apache.commons.collections4.iterators.TransformIterator; /** * A MultiValueMap decorates another map, allowing it to have * more than one value for a key. *

* A MultiMap is a Map with slightly different semantics. * Putting a value into the map will add the value to a Collection at that key. * Getting a value will return a Collection, holding all the values put to that key. *

* This implementation is a decorator, allowing any Map implementation * to be used as the base. *

* In addition, this implementation allows the type of collection used * for the values to be controlled. By default, an ArrayList * is used, however a Class to instantiate may be specified, * or a factory that returns a Collection instance. *

* Note that MultiValueMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. This class may throw exceptions when accessed * by concurrent threads without synchronization. * * @since 3.2 * @version $Id: MultiValueMap.java 1542763 2013-11-17 17:10:33Z tn $ */ public class MultiValueMap extends AbstractMapDecorator implements MultiMap, Serializable { /** Serialization version */ private static final long serialVersionUID = -2214159910087182007L; /** The factory for creating value collections. */ private final Factory> collectionFactory; /** The cached values. */ private transient Collection valuesView; /** * Creates a map which wraps the given map and * maps keys to ArrayLists. * * @param the key type * @param the value type * @param map the map to wrap * @return a new multi-value map * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static MultiValueMap multiValueMap(final Map> map) { return MultiValueMap. multiValueMap((Map) map, ArrayList.class); } /** * Creates a map which decorates the given map and * maps keys to collections of type collectionClass. * * @param the key type * @param the value type * @param the collection class type * @param map the map to wrap * @param collectionClass the type of the collection class * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Class collectionClass) { return new MultiValueMap(map, new ReflectionFactory(collectionClass)); } /** * Creates a map which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory (must return a Collection object). * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Factory collectionFactory) { return new MultiValueMap(map, collectionFactory); } //----------------------------------------------------------------------- /** * Creates a MultiValueMap based on a HashMap and * storing the multiple values in an ArrayList. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public MultiValueMap() { this(new HashMap(), new ReflectionFactory(ArrayList.class)); } /** * Creates a MultiValueMap which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory which must return a Collection instance */ @SuppressWarnings("unchecked") protected > MultiValueMap(final Map map, final Factory collectionFactory) { super((Map) map); if (collectionFactory == null) { throw new IllegalArgumentException("The factory must not be null"); } this.collectionFactory = collectionFactory; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 4.0 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 4.0 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Clear the map. */ @Override public void clear() { // If you believe that you have GC issues here, try uncommenting this code // Set pairs = getMap().entrySet(); // Iterator pairsIterator = pairs.iterator(); // while (pairsIterator.hasNext()) { // Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); // Collection coll = (Collection) keyValuePair.getValue(); // coll.clear(); // } decorated().clear(); } /** * Removes a specific value from map. *

* The item is removed from the collection mapped to the specified key. * Other values attached to that key are unaffected. *

* If the last value for a key is removed, null will be returned * from a subsequent get(key). * * @param key the key to remove from * @param value the value to remove * @return {@code true} if the mapping was removed, {@code false} otherwise */ public boolean removeMapping(final Object key, final Object value) { final Collection valuesForKey = getCollection(key); if (valuesForKey == null) { return false; } final boolean removed = valuesForKey.remove(value); if (removed == false) { return false; } if (valuesForKey.isEmpty()) { remove(key); } return true; } /** * Checks whether the map contains the value specified. *

* This checks all collections against all keys for the value, and thus could be slow. * * @param value the value to search for * @return true if the map contains the value */ @Override @SuppressWarnings("unchecked") public boolean containsValue(final Object value) { final Set> pairs = decorated().entrySet(); if (pairs != null) { for (final Map.Entry entry : pairs) { if (((Collection) entry.getValue()).contains(value)) { return true; } } } return false; } /** * Adds the value to the collection associated with the specified key. *

* Unlike a normal Map the previous value is not replaced. * Instead the new value is added to the collection stored against the key. * * @param key the key to store against * @param value the value to add to the collection at the key * @return the value added if the map changed and null if the map did not change */ @Override @SuppressWarnings("unchecked") public Object put(final K key, final Object value) { boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(1); // might produce a non-empty collection coll.add((V) value); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.add((V) value); } return result ? value : null; } /** * Override superclass to ensure that MultiMap instances are * correctly handled. *

* If you call this method with a normal map, each entry is * added using put(Object,Object). * If you call this method with a multi map, each entry is * added using putAll(Object,Collection). * * @param map the map to copy (either a normal or multi map) */ @Override @SuppressWarnings("unchecked") public void putAll(final Map map) { if (map instanceof MultiMap) { for (final Map.Entry entry : ((MultiMap) map).entrySet()) { putAll(entry.getKey(), (Collection) entry.getValue()); } } else { for (final Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } } /** * {@inheritDoc} *

* NOTE: the returned Entry objects will contain as value a {@link Collection} * of all values that are mapped to the given key. To get a "flattened" version * of all mappings contained in this map, use {@link #iterator()}. * * @see #iterator() */ @Override public Set> entrySet() { return super.entrySet(); } /** * Gets a collection containing all the values in the map. *

* This returns a collection containing the combination of values from all keys. * * @return a collection view of the values contained in this map */ @Override @SuppressWarnings("unchecked") public Collection values() { final Collection vs = valuesView; return (Collection) (vs != null ? vs : (valuesView = new Values())); } /** * Checks whether the collection at the specified key contains the value. * * @param key the key to search for * @param value the value to search for * @return true if the map contains the value */ public boolean containsValue(final Object key, final Object value) { final Collection coll = getCollection(key); if (coll == null) { return false; } return coll.contains(value); } /** * Gets the collection mapped to the specified key. * This method is a convenience method to typecast the result of get(key). * * @param key the key to retrieve * @return the collection mapped to the key, null if no mapping */ @SuppressWarnings("unchecked") public Collection getCollection(final Object key) { return (Collection) decorated().get(key); } /** * Gets the size of the collection mapped to the specified key. * * @param key the key to get size for * @return the size of the collection at the key, zero if key not in map */ public int size(final Object key) { final Collection coll = getCollection(key); if (coll == null) { return 0; } return coll.size(); } /** * Adds a collection of values to the collection associated with * the specified key. * * @param key the key to store against * @param values the values to add to the collection at the key, null ignored * @return true if this map changed */ public boolean putAll(final K key, final Collection values) { if (values == null || values.size() == 0) { return false; } boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(values.size()); // might produce a non-empty collection coll.addAll(values); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.addAll(values); } return result; } /** * Gets an iterator for the collection mapped to the specified key. * * @param key the key to get an iterator for * @return the iterator of the collection at the key, empty iterator if key not in map */ public Iterator iterator(final Object key) { if (!containsKey(key)) { return EmptyIterator.emptyIterator(); } return new ValuesIterator(key); } /** * Gets an iterator for all mappings stored in this {@link MultiValueMap}. *

* The iterator will return multiple Entry objects with the same key * if there are multiple values mapped to this key. *

* NOTE: calling {@link java.util.Map.Entry#setValue(Object)} on any of the returned * elements will result in a {@link UnsupportedOperationException}. * * @return the iterator of all mappings in this map * @since 4.0 */ public Iterator> iterator() { final Collection allKeys = new ArrayList(keySet()); final Iterator keyIterator = allKeys.iterator(); return new LazyIteratorChain>() { @Override protected Iterator> nextIterator(int count) { if ( ! keyIterator.hasNext() ) { return null; } final K key = keyIterator.next(); final Transformer> transformer = new Transformer>() { public Entry transform(final V input) { return new Entry() { public K getKey() { return key; } public V getValue() { return input; } public V setValue(V value) { throw new UnsupportedOperationException(); } }; } }; return new TransformIterator>(new ValuesIterator(key), transformer); } }; } /** * Gets the total size of the map by counting all the values. * * @return the total size of the map counting all values */ public int totalSize() { int total = 0; for (final Object v : decorated().values()) { total += CollectionUtils.size(v); } return total; } /** * Creates a new instance of the map value Collection container * using the factory. *

* This method can be overridden to perform your own processing * instead of using the factory. * * @param size the collection size that is about to be added * @return the new collection */ protected Collection createCollection(final int size) { return collectionFactory.create(); } //----------------------------------------------------------------------- /** * Inner class that provides the values view. */ private class Values extends AbstractCollection { @Override public Iterator iterator() { final IteratorChain chain = new IteratorChain(); for (final K k : keySet()) { chain.addIterator(new ValuesIterator(k)); } return chain; } @Override public int size() { return totalSize(); } @Override public void clear() { MultiValueMap.this.clear(); } } /** * Inner class that provides the values iterator. */ private class ValuesIterator implements Iterator { private final Object key; private final Collection values; private final Iterator iterator; public ValuesIterator(final Object key) { this.key = key; this.values = getCollection(key); this.iterator = values.iterator(); } public void remove() { iterator.remove(); if (values.isEmpty()) { MultiValueMap.this.remove(key); } } public boolean hasNext() { return iterator.hasNext(); } public V next() { return iterator.next(); } } /** * Inner class that provides a simple reflection factory. */ private static class ReflectionFactory> implements Factory, Serializable { /** Serialization version */ private static final long serialVersionUID = 2986114157496788874L; private final Class clazz; public ReflectionFactory(final Class clazz) { this.clazz = clazz; } public T create() { try { return clazz.newInstance(); } catch (final Exception ex) { throw new FunctorException("Cannot instantiate class: " + clazz, ex); } } } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 6119; to = 6082.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Factory; import org.apache.commons.collections4.FunctorException; import org.apache.commons.collections4.MultiMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.iterators.EmptyIterator; import org.apache.commons.collections4.iterators.IteratorChain; import org.apache.commons.collections4.iterators.LazyIteratorChain; import org.apache.commons.collections4.iterators.TransformIterator; /** * A MultiValueMap decorates another map, allowing it to have * more than one value for a key. *

* A MultiMap is a Map with slightly different semantics. * Putting a value into the map will add the value to a Collection at that key. * Getting a value will return a Collection, holding all the values put to that key. *

* This implementation is a decorator, allowing any Map implementation * to be used as the base. *

* In addition, this implementation allows the type of collection used * for the values to be controlled. By default, an ArrayList * is used, however a Class to instantiate may be specified, * or a factory that returns a Collection instance. *

* Note that MultiValueMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. This class may throw exceptions when accessed * by concurrent threads without synchronization. * * @since 3.2 * @version $Id: MultiValueMap.java 1542763 2013-11-17 17:10:33Z tn $ */ public class MultiValueMap extends AbstractMapDecorator implements MultiMap, Serializable { /** Serialization version */ private static final long serialVersionUID = -2214159910087182007L; /** The factory for creating value collections. */ private final Factory> collectionFactory; /** The cached values. */ private transient Collection valuesView; /** * Creates a map which wraps the given map and * maps keys to ArrayLists. * * @param the key type * @param the value type * @param map the map to wrap * @return a new multi-value map * @since 4.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static MultiValueMap multiValueMap(final Map> map) { return MultiValueMap. multiValueMap((Map) map, ArrayList.class); } /** * Creates a map which decorates the given map and * maps keys to collections of type collectionClass. * * @param the key type * @param the value type * @param the collection class type * @param map the map to wrap * @param collectionClass the type of the collection class * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Class collectionClass) { return new MultiValueMap(map, new ReflectionFactory(collectionClass)); } /** * Creates a map which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the key type * @param the value type * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory (must return a Collection object). * @return a new multi-value map * @since 4.0 */ public static > MultiValueMap multiValueMap(final Map map, final Factory collectionFactory) { return new MultiValueMap(map, collectionFactory); } //----------------------------------------------------------------------- /** * Creates a MultiValueMap based on a HashMap and * storing the multiple values in an ArrayList. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public MultiValueMap() { this(new HashMap(), new ReflectionFactory(ArrayList.class)); } /** * Creates a MultiValueMap which decorates the given map and * creates the value collections using the supplied collectionFactory. * * @param the collection class type * @param map the map to decorate * @param collectionFactory the collection factory which must return a Collection instance */ @SuppressWarnings("unchecked") protected > MultiValueMap(final Map map, final Factory collectionFactory) { super((Map) map); if (collectionFactory == null) { throw new IllegalArgumentException("The factory must not be null"); } this.collectionFactory = collectionFactory; } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 4.0 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 4.0 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Clear the map. */ @Override public void clear() { // If you believe that you have GC issues here, try uncommenting this code // Set pairs = getMap().entrySet(); // Iterator pairsIterator = pairs.iterator(); // while (pairsIterator.hasNext()) { // Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); // Collection coll = (Collection) keyValuePair.getValue(); // coll.clear(); // } decorated().clear(); } /** * Removes a specific value from map. *

* The item is removed from the collection mapped to the specified key. * Other values attached to that key are unaffected. *

* If the last value for a key is removed, null will be returned * from a subsequent get(key). * * @param key the key to remove from * @param value the value to remove * @return {@code true} if the mapping was removed, {@code false} otherwise */ public boolean removeMapping(final Object key, final Object value) { final Collection valuesForKey = getCollection(key); if (valuesForKey == null) { return false; } final boolean removed = valuesForKey.remove(value); if (removed == false) { return false; } if (valuesForKey.isEmpty()) { remove(key); } return true; } /** * Checks whether the map contains the value specified. *

* This checks all collections against all keys for the value, and thus could be slow. * * @param value the value to search for * @return true if the map contains the value */ @Override @SuppressWarnings("unchecked") public boolean containsValue(final Object value) { final Set> pairs = decorated().entrySet(); if (pairs != null) { for (final Map.Entry entry : pairs) { if (((Collection) entry.getValue()).contains(value)) { return true; } } } return false; } /** * Adds the value to the collection associated with the specified key. *

* Unlike a normal Map the previous value is not replaced. * Instead the new value is added to the collection stored against the key. * * @param key the key to store against * @param value the value to add to the collection at the key * @return the value added if the map changed and null if the map did not change */ @Override @SuppressWarnings("unchecked") public Object put(final K key, final Object value) { boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(1); // might produce a non-empty collection coll.add((V) value); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.add((V) value); } return result ? value : null; } /** * Override superclass to ensure that MultiMap instances are * correctly handled. *

* If you call this method with a normal map, each entry is * added using put(Object,Object). * If you call this method with a multi map, each entry is * added using putAll(Object,Collection). * * @param map the map to copy (either a normal or multi map) */ @Override @SuppressWarnings("unchecked") public void putAll(final Map map) { if (map instanceof MultiMap) { for (final Map.Entry entry : ((MultiMap) map).entrySet()) { putAll(entry.getKey(), (Collection) entry.getValue()); } } else { for (final Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } } /** * {@inheritDoc} *

* NOTE: the returned Entry objects will contain as value a {@link Collection} * of all values that are mapped to the given key. To get a "flattened" version * of all mappings contained in this map, use {@link #iterator()}. * * @see #iterator() */ @Override public Set> entrySet() { return super.entrySet(); } /** * Gets a collection containing all the values in the map. *

* This returns a collection containing the combination of values from all keys. * * @return a collection view of the values contained in this map */ @Override @SuppressWarnings("unchecked") public Collection values() { final Collection vs = valuesView; return (Collection) (vs != null ? vs : (valuesView = new Values())); } /** * Checks whether the collection at the specified key contains the value. * * @param key the key to search for * @param value the value to search for * @return true if the map contains the value */ public boolean containsValue(final Object key, final Object value) { final Collection coll = getCollection(key); if (coll == null) { return false; } return coll.contains(value); } /** * Gets the collection mapped to the specified key. * This method is a convenience method to typecast the result of get(key). * * @param key the key to retrieve * @return the collection mapped to the key, null if no mapping */ @SuppressWarnings("unchecked") public Collection getCollection(final Object key) { return (Collection) decorated().get(key); } /** * Gets the size of the collection mapped to the specified key. * * @param key the key to get size for * @return the size of the collection at the key, zero if key not in map */ public int size(final Object key) { final Collection coll = getCollection(key); if (coll == null) { return 0; } return coll.size(); } /** * Adds a collection of values to the collection associated with * the specified key. * * @param key the key to store against * @param values the values to add to the collection at the key, null ignored * @return true if this map changed */ public boolean putAll(final K key, final Collection values) { if (values == null || values.size() == 0) { return false; } boolean result = false; Collection coll = getCollection(key); if (coll == null) { coll = createCollection(values.size()); // might produce a non-empty collection coll.addAll(values); if (coll.size() > 0) { // only add if non-zero size to maintain class state decorated().put(key, coll); result = true; // map definitely changed } } else { result = coll.addAll(values); } return result; } /** * Gets an iterator for the collection mapped to the specified key. * * @param key the key to get an iterator for * @return the iterator of the collection at the key, empty iterator if key not in map */ public Iterator iterator(final Object key) { if (!containsKey(key)) { return EmptyIterator.emptyIterator(); } return new ValuesIterator(key); } /** * Gets an iterator for all mappings stored in this {@link MultiValueMap}. *

* The iterator will return multiple Entry objects with the same key * if there are multiple values mapped to this key. *

* NOTE: calling {@link java.util.Map.Entry#setValue(Object)} on any of the returned * elements will result in a {@link UnsupportedOperationException}. * * @return the iterator of all mappings in this map * @since 4.0 */ public Iterator> iterator() { final Collection allKeys = new ArrayList(keySet()); final Iterator keyIterator = allKeys.iterator(); return new LazyIteratorChain>() { @Override protected Iterator> nextIterator(int count) { if ( ! keyIterator.hasNext() ) { return null; } final K key = keyIterator.next(); final Transformer> transformer = new Transformer>() { public Entry transform(final V input) { return new Entry() { public K getKey() { return key; } public V getValue() { return input; } public V setValue(V value) { throw new UnsupportedOperationException(); } }; } }; return new TransformIterator>(new ValuesIterator(key), transformer); } }; } /** * Gets the total size of the map by counting all the values. * * @return the total size of the map counting all values */ public int totalSize() { int total = 0; for (final Object v : decorated().values()) { total += CollectionUtils.size(v); } return total; } /** * Creates a new instance of the map value Collection container * using the factory. *

* This method can be overridden to perform your own processing * instead of using the factory. * * @param size the collection size that is about to be added * @return the new collection */ protected Collection createCollection(final int size) { return collectionFactory.create(); } //----------------------------------------------------------------------- /** * Inner class that provides the values view. */ private class Values extends AbstractCollection { @Override public Iterator iterator() { final IteratorChain chain = new IteratorChain(); for (final K k : keySet()) { chain.addIterator(new ValuesIterator(k)); } return chain; } @Override public int size() { return totalSize(); } @Override public void clear() { MultiValueMap.this.clear(); } } /** * Inner class that provides the values iterator. */ private class ValuesIterator implements Iterator { private final Object key; private final Collection values; private final Iterator iterator; public ValuesIterator(final Object key) { this.key = key; this.values = getCollection(key); this.iterator = values.iterator(); } public void remove() { iterator.remove(); if (values.isEmpty()) { MultiValueMap.this.remove(key); } } public boolean hasNext() { return iterator.hasNext(); } public V next() { return iterator.next(); } } /** * Inner class that provides a simple reflection factory. */ private static class ReflectionFactory> implements Factory, Serializable { /** Serialization version */ private static final long serialVersionUID = 2986114157496788874L; private final Class clazz; public ReflectionFactory(final Class clazz) { this.clazz = clazz; } public T create() { try { return clazz.newInstance(); } catch (final Exception ex) { throw new FunctorException("Cannot instantiate class: " + clazz, ex); } } } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 7103; to = 7015.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Iterator; import java.util.Map; import org.apache.commons.collections4.Predicate; /** * Decorates another Map to validate that additions * match a specified predicate. *

* This map exists to provide validation for the decorated map. * It is normally created to decorate an empty map. * If an object cannot be added to the map, an IllegalArgumentException is thrown. *

* One usage would be to ensure that no null keys are added to the map. *

Map map = PredicatedSet.decorate(new HashMap(), NotNullPredicate.INSTANCE, null);
*

* Note that PredicatedMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. The simplest approach is to wrap this map * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw * exceptions when accessed by concurrent threads without synchronization. *

* This class is Serializable from Commons Collections 3.1. * * @since 3.0 * @version $Id: PredicatedMap.java 1479407 2013-05-05 22:07:53Z tn $ */ public class PredicatedMap extends AbstractInputCheckedMapDecorator implements Serializable { /** Serialization version */ private static final long serialVersionUID = 7412622456128415156L; /** The key predicate to use */ protected final Predicate keyPredicate; /** The value predicate to use */ protected final Predicate valuePredicate; /** * Factory method to create a predicated (validating) map. *

* If there are any elements already in the list being decorated, they * are validated. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @param keyPredicate the predicate to validate the keys, null means no check * @param valuePredicate the predicate to validate to values, null means no check * @return a new predicated map * @throws IllegalArgumentException if the map is null * @since 4.0 */ public static PredicatedMap predicatedMap(final Map map, final Predicate keyPredicate, final Predicate valuePredicate) { return new PredicatedMap(map, keyPredicate, valuePredicate); } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). * * @param map the map to decorate, must not be null * @param keyPredicate the predicate to validate the keys, null means no check * @param valuePredicate the predicate to validate to values, null means no check * @throws IllegalArgumentException if the map is null */ protected PredicatedMap(final Map map, final Predicate keyPredicate, final Predicate valuePredicate) { super(map); this.keyPredicate = keyPredicate; this.valuePredicate = valuePredicate; final Iterator> it = map.entrySet().iterator(); while (it.hasNext()) { final Map.Entry entry = it.next(); validate(entry.getKey(), entry.getValue()); } } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 3.1 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 3.1 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Validates a key value pair. * * @param key the key to validate * @param value the value to validate * @throws IllegalArgumentException if invalid */ protected void validate(final K key, final V value) { if (keyPredicate != null && keyPredicate.evaluate(key) == false) { throw new IllegalArgumentException("Cannot add key - Predicate rejected it"); } if (valuePredicate != null && valuePredicate.evaluate(value) == false) { throw new IllegalArgumentException("Cannot add value - Predicate rejected it"); } } /** * Override to validate an object set into the map via setValue. * * @param value the value to validate * @return the value itself * @throws IllegalArgumentException if invalid * @since 3.1 */ @Override protected V checkSetValue(final V value) { if (valuePredicate.evaluate(value) == false) { throw new IllegalArgumentException("Cannot set value - Predicate rejected it"); } return value; } /** * Override to only return true when there is a value transformer. * * @return true if a value predicate is in use * @since 3.1 */ @Override protected boolean isSetValueChecking() { return valuePredicate != null; } //----------------------------------------------------------------------- @Override public V put(final K key, final V value) { validate(key, value); return map.put(key, value); } @Override public void putAll(final Map mapToCopy) { for (final Map.Entry entry : mapToCopy.entrySet()) { validate(entry.getKey(), entry.getValue()); } super.putAll(mapToCopy); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 5097; to = 5013.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Iterator; import java.util.Map; import org.apache.commons.collections4.Predicate; /** * Decorates another Map to validate that additions * match a specified predicate. *

* This map exists to provide validation for the decorated map. * It is normally created to decorate an empty map. * If an object cannot be added to the map, an IllegalArgumentException is thrown. *

* One usage would be to ensure that no null keys are added to the map. *

Map map = PredicatedSet.decorate(new HashMap(), NotNullPredicate.INSTANCE, null);
*

* Note that PredicatedMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. The simplest approach is to wrap this map * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw * exceptions when accessed by concurrent threads without synchronization. *

* This class is Serializable from Commons Collections 3.1. * * @since 3.0 * @version $Id: PredicatedMap.java 1479407 2013-05-05 22:07:53Z tn $ */ public class PredicatedMap extends AbstractInputCheckedMapDecorator implements Serializable { /** Serialization version */ private static final long serialVersionUID = 7412622456128415156L; /** The key predicate to use */ protected final Predicate keyPredicate; /** The value predicate to use */ protected final Predicate valuePredicate; /** * Factory method to create a predicated (validating) map. *

* If there are any elements already in the list being decorated, they * are validated. * * @param the key type * @param the value type * @param map the map to decorate, must not be null * @param keyPredicate the predicate to validate the keys, null means no check * @param valuePredicate the predicate to validate to values, null means no check * @return a new predicated map * @throws IllegalArgumentException if the map is null * @since 4.0 */ public static PredicatedMap predicatedMap(final Map map, final Predicate keyPredicate, final Predicate valuePredicate) { return new PredicatedMap(map, keyPredicate, valuePredicate); } //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). * * @param map the map to decorate, must not be null * @param keyPredicate the predicate to validate the keys, null means no check * @param valuePredicate the predicate to validate to values, null means no check * @throws IllegalArgumentException if the map is null */ protected PredicatedMap(final Map map, final Predicate keyPredicate, final Predicate valuePredicate) { super(map); this.keyPredicate = keyPredicate; this.valuePredicate = valuePredicate; final Iterator> it = map.entrySet().iterator(); while (it.hasNext()) { final Map.Entry entry = it.next(); validate(entry.getKey(), entry.getValue()); } } //----------------------------------------------------------------------- /** * Write the map out using a custom routine. * * @param out the output stream * @throws IOException * @since 3.1 */ private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(map); } /** * Read the map in using a custom routine. * * @param in the input stream * @throws IOException * @throws ClassNotFoundException * @since 3.1 */ @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); map = (Map) in.readObject(); // (1) } //----------------------------------------------------------------------- /** * Validates a key value pair. * * @param key the key to validate * @param value the value to validate * @throws IllegalArgumentException if invalid */ protected void validate(final K key, final V value) { if (keyPredicate != null && keyPredicate.evaluate(key) == false) { throw new IllegalArgumentException("Cannot add key - Predicate rejected it"); } if (valuePredicate != null && valuePredicate.evaluate(value) == false) { throw new IllegalArgumentException("Cannot add value - Predicate rejected it"); } } /** * Override to validate an object set into the map via setValue. * * @param value the value to validate * @return the value itself * @throws IllegalArgumentException if invalid * @since 3.1 */ @Override protected V checkSetValue(final V value) { if (valuePredicate.evaluate(value) == false) { throw new IllegalArgumentException("Cannot set value - Predicate rejected it"); } return value; } /** * Override to only return true when there is a value transformer. * * @return true if a value predicate is in use * @since 3.1 */ @Override protected boolean isSetValueChecking() { return valuePredicate != null; } //----------------------------------------------------------------------- @Override public V put(final K key, final V value) { validate(key, value); return map.put(key, value); } @Override public void putAll(final Map mapToCopy) { for (final Map.Entry entry : mapToCopy.entrySet()) { validate(entry.getKey(), entry.getValue()); } super.putAll(mapToCopy); } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 5097; to = 5013.Please, attach your messages.log to new issue! ----- /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections4.map; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import org.apache.commons.collections4.MapIterator; import org.apache.commons.collections4.keyvalue.DefaultMapEntry; /** * An abstract implementation of a hash-based map that allows the entries to * be removed by the garbage collector. *

* This class implements all the features necessary for a subclass reference * hash-based map. Key-value entries are stored in instances of the * ReferenceEntry class which can be overridden and replaced. * The iterators can similarly be replaced, without the need to replace the KeySet, * EntrySet and Values view classes. *

* Overridable methods are provided to change the default hashing behaviour, and * to change how entries are added to and removed from the map. Hopefully, all you * need for unusual subclasses is here. *

* When you construct an AbstractReferenceMap, you can specify what * kind of references are used to store the map's keys and values. * If non-hard references are used, then the garbage collector can remove * mappings if a key or value becomes unreachable, or if the JVM's memory is * running low. For information on how the different reference types behave, * see {@link Reference}. *

* Different types of references can be specified for keys and values. * The keys can be configured to be weak but the values hard, * in which case this class will behave like a * * WeakHashMap. However, you can also specify hard keys and * weak values, or any other combination. The default constructor uses * hard keys and soft values, providing a memory-sensitive cache. *

* This {@link Map} implementation does not allow null elements. * Attempting to add a null key or value to the map will raise a * NullPointerException. *

* All the available iterators can be reset back to the start by casting to * ResettableIterator and calling reset(). *

* This implementation is not synchronized. * You can use {@link java.util.Collections#synchronizedMap} to * provide synchronized access to a ReferenceMap. * * @see java.lang.ref.Reference * @since 3.1 (extracted from ReferenceMap in 3.0) * @version $Id: AbstractReferenceMap.java 1477799 2013-04-30 19:56:11Z tn $ */ public abstract class AbstractReferenceMap extends AbstractHashedMap { /** * Reference type enum. */ public static enum ReferenceStrength { HARD(0), SOFT(1), WEAK(2); /** value */ public final int value; /** * Resolve enum from int. * @param value the int value * @return ReferenceType * @throws IllegalArgumentException if the specified value is invalid. */ public static ReferenceStrength resolve(final int value) { switch (value) { case 0: return HARD; case 1: return SOFT; case 2: return WEAK; default: throw new IllegalArgumentException(); } } private ReferenceStrength(final int value) { this.value = value; } } /** * The reference type for keys. */ private ReferenceStrength keyType; /** * The reference type for values. */ private ReferenceStrength valueType; /** * Should the value be automatically purged when the associated key has been collected? */ private boolean purgeValues; /** * ReferenceQueue used to eliminate stale mappings. * See purge. */ private transient ReferenceQueue queue; //----------------------------------------------------------------------- /** * Constructor used during deserialization. */ protected AbstractReferenceMap() { super(); } /** * Constructs a new empty map with the specified reference types, * load factor and initial capacity. * * @param keyType the type of reference to use for keys; * must be {@link ReferenceStrength#HARD HARD}, * {@link ReferenceStrength#SOFT SOFT}, * {@link ReferenceStrength#WEAK WEAK} * @param valueType the type of reference to use for values; * must be {@link ReferenceStrength#HARD}, * {@link ReferenceStrength#SOFT SOFT}, * {@link ReferenceStrength#WEAK WEAK} * @param capacity the initial capacity for the map * @param loadFactor the load factor for the map * @param purgeValues should the value be automatically purged when the * key is garbage collected */ protected AbstractReferenceMap( final ReferenceStrength keyType, final ReferenceStrength valueType, final int capacity, final float loadFactor, final boolean purgeValues) { super(capacity, loadFactor); this.keyType = keyType; this.valueType = valueType; this.purgeValues = purgeValues; } /** * Initialise this subclass during construction, cloning or deserialization. */ @Override protected void init() { queue = new ReferenceQueue(); } //----------------------------------------------------------------------- /** * Gets the size of the map. * * @return the size */ @Override public int size() { purgeBeforeRead(); return super.size(); } /** * Checks whether the map is currently empty. * * @return true if the map is currently size zero */ @Override public boolean isEmpty() { purgeBeforeRead(); return super.isEmpty(); } /** * Checks whether the map contains the specified key. * * @param key the key to search for * @return true if the map contains the key */ @Override public boolean containsKey(final Object key) { purgeBeforeRead(); final Entry entry = getEntry(key); if (entry == null) { return false; } return entry.getValue() != null; } /** * Checks whether the map contains the specified value. * * @param value the value to search for * @return true if the map contains the value */ @Override public boolean containsValue(final Object value) { purgeBeforeRead(); if (value == null) { return false; } return super.containsValue(value); } /** * Gets the value mapped to the key specified. * * @param key the key * @return the mapped value, null if no match */ @Override public V get(final Object key) { purgeBeforeRead(); final Entry entry = getEntry(key); if (entry == null) { return null; } return entry.getValue(); } /** * Puts a key-value mapping into this map. * Neither the key nor the value may be null. * * @param key the key to add, must not be null * @param value the value to add, must not be null * @return the value previously mapped to this key, null if none * @throws NullPointerException if either the key or value is null */ @Override public V put(final K key, final V value) { if (key == null) { throw new NullPointerException("null keys not allowed"); } if (value == null) { throw new NullPointerException("null values not allowed"); } purgeBeforeWrite(); return super.put(key, value); } /** * Removes the specified mapping from this map. * * @param key the mapping to remove * @return the value mapped to the removed key, null if key not in map */ @Override public V remove(final Object key) { if (key == null) { return null; } purgeBeforeWrite(); return super.remove(key); } /** * Clears this map. */ @Override public void clear() { super.clear(); while (queue.poll() != null) {} // drain the queue } //----------------------------------------------------------------------- /** * Gets a MapIterator over the reference map. * The iterator only returns valid key/value pairs. * * @return a map iterator */ @Override public MapIterator mapIterator() { return new ReferenceMapIterator(this); } /** * Returns a set view of this map's entries. * An iterator returned entry is valid until next() is called again. * The setValue() method on the toArray entries has no effect. * * @return a set view of this map's entries */ @Override public Set> entrySet() { if (entrySet == null) { entrySet = new ReferenceEntrySet(this); } return entrySet; } /** * Returns a set view of this map's keys. * * @return a set view of this map's keys */ @Override public Set keySet() { if (keySet == null) { keySet = new ReferenceKeySet(this); } return keySet; } /** * Returns a collection view of this map's values. * * @return a set view of this map's values */ @Override public Collection values() { if (values == null) { values = new ReferenceValues(this); } return values; } //----------------------------------------------------------------------- /** * Purges stale mappings from this map before read operations. *

* This implementation calls {@link #purge()} to maintain a consistent state. */ protected void purgeBeforeRead() { purge(); } /** * Purges stale mappings from this map before write operations. *

* This implementation calls {@link #purge()} to maintain a consistent state. */ protected void purgeBeforeWrite() { purge(); } /** * Purges stale mappings from this map. *

* Note that this method is not synchronized! Special * care must be taken if, for instance, you want stale * mappings to be removed on a periodic basis by some * background thread. */ protected void purge() { Reference ref = queue.poll(); while (ref != null) { purge(ref); ref = queue.poll(); } } /** * Purges the specified reference. * * @param ref the reference to purge */ protected void purge(final Reference ref) { // The hashCode of the reference is the hashCode of the // mapping key, even if the reference refers to the // mapping value... final int hash = ref.hashCode(); final int index = hashIndex(hash, data.length); HashEntry previous = null; HashEntry entry = data[index]; while (entry != null) { if (((ReferenceEntry) entry).purge(ref)) { if (previous == null) { data[index] = entry.next; } else { previous.next = entry.next; } this.size--; return; } previous = entry; entry = entry.next; } } //----------------------------------------------------------------------- /** * Gets the entry mapped to the key specified. * * @param key the key * @return the entry, null if no match */ @Override protected HashEntry getEntry(final Object key) { if (key == null) { return null; } return super.getEntry(key); } /** * Gets the hash code for a MapEntry. * Subclasses can override this, for example to use the identityHashCode. * * @param key the key to get a hash code for, may be null * @param value the value to get a hash code for, may be null * @return the hash code, as per the MapEntry specification */ protected int hashEntry(final Object key, final Object value) { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } /** * Compares two keys, in internal converted form, to see if they are equal. *

* This implementation converts the key from the entry to a real reference * before comparison. * * @param key1 the first key to compare passed in from outside * @param key2 the second key extracted from the entry via entry.key * @return true if equal */ @Override @SuppressWarnings("unchecked") protected boolean isEqualKey(final Object key1, Object key2) { key2 = keyType == ReferenceStrength.HARD ? key2 : ((Reference) key2).get(); return key1 == key2 || key1.equals(key2); } /** * Creates a ReferenceEntry instead of a HashEntry. * * @param next the next entry in sequence * @param hashCode the hash code to use * @param key the key to store * @param value the value to store * @return the newly created entry */ @Override protected ReferenceEntry createEntry(final HashEntry next, final int hashCode, final K key, final V value) { return new ReferenceEntry(this, next, hashCode, key, value); } /** * Creates an entry set iterator. * * @return the entrySet iterator */ @Override protected Iterator> createEntrySetIterator() { return new ReferenceEntrySetIterator(this); } /** * Creates an key set iterator. * * @return the keySet iterator */ @Override protected Iterator createKeySetIterator() { return new ReferenceKeySetIterator(this); } /** * Creates an values iterator. * * @return the values iterator */ @Override protected Iterator createValuesIterator() { return new ReferenceValuesIterator(this); } //----------------------------------------------------------------------- /** * EntrySet implementation. */ static class ReferenceEntrySet extends EntrySet { protected ReferenceEntrySet(final AbstractHashedMap parent) { super(parent); } @Override public Object[] toArray() { return toArray(new Object[size()]); } @Override public T[] toArray(final T[] arr) { // special implementation to handle disappearing entries final ArrayList> list = new ArrayList>(size()); for (final Map.Entry entry : this) { list.add(new DefaultMapEntry(entry)); } return list.toArray(arr); } } //----------------------------------------------------------------------- /** * KeySet implementation. */ static class ReferenceKeySet extends KeySet { protected ReferenceKeySet(final AbstractHashedMap parent) { super(parent); } @Override public Object[] toArray() { return toArray(new Object[size()]); } @Override public T[] toArray(final T[] arr) { // special implementation to handle disappearing keys final List list = new ArrayList(size()); for (final K key : this) { list.add(key); } return list.toArray(arr); } } //----------------------------------------------------------------------- /** * Values implementation. */ static class ReferenceValues extends Values { protected ReferenceValues(final AbstractHashedMap parent) { super(parent); } @Override public Object[] toArray() { return toArray(new Object[size()]); } @Override public T[] toArray(final T[] arr) { // special implementation to handle disappearing values final List list = new ArrayList(size()); for (final V value : this) { list.add(value); } return list.toArray(arr); } } //----------------------------------------------------------------------- /** * A MapEntry implementation for the map. *

* If getKey() or getValue() returns null, it means * the mapping is stale and should be removed. * * @since 3.1 */ protected static class ReferenceEntry extends HashEntry { /** The parent map */ private final AbstractReferenceMap parent; /** * Creates a new entry object for the ReferenceMap. * * @param parent the parent map * @param next the next entry in the hash bucket * @param hashCode the hash code of the key * @param key the key * @param value the value */ public ReferenceEntry(final AbstractReferenceMap parent, final HashEntry next, final int hashCode, final K key, final V value) { super(next, hashCode, null, null); this.parent = parent; this.key = toReference(parent.keyType, key, hashCode); this.value = toReference(parent.valueType, value, hashCode); // the key hashCode is passed in deliberately } /** * Gets the key from the entry. * This method dereferences weak and soft keys and thus may return null. * * @return the key, which may be null if it was garbage collected */ @Override @SuppressWarnings("unchecked") public K getKey() { return (K) (parent.keyType == ReferenceStrength.HARD ? key : ((Reference) key).get()); } /** * Gets the value from the entry. * This method dereferences weak and soft value and thus may return null. * * @return the value, which may be null if it was garbage collected */ @Override @SuppressWarnings("unchecked") public V getValue() { return (V) (parent.valueType == ReferenceStrength.HARD ? value : ((Reference) value).get()); } /** * Sets the value of the entry. * * @param obj the object to store * @return the previous value */ @Override @SuppressWarnings("unchecked") public V setValue(final V obj) { final V old = getValue(); if (parent.valueType != ReferenceStrength.HARD) { ((Reference) value).clear(); } value = toReference(parent.valueType, obj, hashCode); return old; } /** * Compares this map entry to another. *

* This implementation uses isEqualKey and * isEqualValue on the main map for comparison. * * @param obj the other map entry to compare to * @return true if equal, false if not */ @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (obj instanceof Map.Entry == false) { return false; } final Map.Entry entry = (Map.Entry)obj; final Object entryKey = entry.getKey(); // convert to hard reference final Object entryValue = entry.getValue(); // convert to hard reference if (entryKey == null || entryValue == null) { return false; } // compare using map methods, aiding identity subclass // note that key is direct access and value is via method return parent.isEqualKey(entryKey, key) && parent.isEqualValue(entryValue, getValue()); } /** * Gets the hashcode of the entry using temporary hard references. *

* This implementation uses hashEntry on the main map. * * @return the hashcode of the entry */ @Override public int hashCode() { return parent.hashEntry(getKey(), getValue()); } /** * Constructs a reference of the given type to the given referent. * The reference is registered with the queue for later purging. * * @param the type of the referenced object * @param type HARD, SOFT or WEAK * @param referent the object to refer to * @param hash the hash code of the key of the mapping; * this number might be different from referent.hashCode() if * the referent represents a value and not a key * @return the reference to the object */ protected Object toReference(final ReferenceStrength type, final T referent, final int hash) { if (type == ReferenceStrength.HARD) { return referent; } if (type == ReferenceStrength.SOFT) { return new SoftRef(hash, referent, parent.queue); } if (type == ReferenceStrength.WEAK) { return new WeakRef(hash, referent, parent.queue); } throw new Error(); } /** * Purges the specified reference * @param ref the reference to purge * @return true or false */ boolean purge(final Reference ref) { boolean r = parent.keyType != ReferenceStrength.HARD && key == ref; r = r || parent.valueType != ReferenceStrength.HARD && value == ref; if (r) { if (parent.keyType != ReferenceStrength.HARD) { ((Reference) key).clear(); } if (parent.valueType != ReferenceStrength.HARD) { ((Reference) value).clear(); } else if (parent.purgeValues) { value = null; } } return r; } /** * Gets the next entry in the bucket. * * @return the next entry in the bucket */ protected ReferenceEntry next() { return (ReferenceEntry) next; } } //----------------------------------------------------------------------- /** * Base iterator class. */ static class ReferenceBaseIterator { /** The parent map */ final AbstractReferenceMap parent; // These fields keep track of where we are in the table. int index; ReferenceEntry entry; ReferenceEntry previous; // These Object fields provide hard references to the // current and next entry; this assures that if hasNext() // returns true, next() will actually return a valid element. K currentKey, nextKey; V currentValue, nextValue; int expectedModCount; public ReferenceBaseIterator(final AbstractReferenceMap parent) { super(); this.parent = parent; index = parent.size() != 0 ? parent.data.length : 0; // have to do this here! size() invocation above // may have altered the modCount. expectedModCount = parent.modCount; } public boolean hasNext() { checkMod(); while (nextNull()) { ReferenceEntry e = entry; int i = index; while (e == null && i > 0) { i--; e = (ReferenceEntry) parent.data[i]; } entry = e; index = i; if (e == null) { currentKey = null; currentValue = null; return false; } nextKey = e.getKey(); nextValue = e.getValue(); if (nextNull()) { entry = entry.next(); } } return true; } private void checkMod() { if (parent.modCount != expectedModCount) { throw new ConcurrentModificationException(); } } private boolean nextNull() { return nextKey == null || nextValue == null; } protected ReferenceEntry nextEntry() { checkMod(); if (nextNull() && !hasNext()) { throw new NoSuchElementException(); } previous = entry; entry = entry.next(); currentKey = nextKey; currentValue = nextValue; nextKey = null; nextValue = null; return previous; } protected ReferenceEntry currentEntry() { checkMod(); return previous; } public void remove() { checkMod(); if (previous == null) { throw new IllegalStateException(); } parent.remove(currentKey); previous = null; currentKey = null; currentValue = null; expectedModCount = parent.modCount; } } /** * The EntrySet iterator. */ static class ReferenceEntrySetIterator extends ReferenceBaseIterator implements Iterator> { public ReferenceEntrySetIterator(final AbstractReferenceMap parent) { super(parent); } public Map.Entry next() { return nextEntry(); } } /** * The keySet iterator. */ static class ReferenceKeySetIterator extends ReferenceBaseIterator implements Iterator { @SuppressWarnings("unchecked") ReferenceKeySetIterator(final AbstractReferenceMap parent) { super((AbstractReferenceMap) parent); } public K next() { return nextEntry().getKey(); } } /** * The values iterator. */ static class ReferenceValuesIterator extends ReferenceBaseIterator implements Iterator { @SuppressWarnings("unchecked") ReferenceValuesIterator(final AbstractReferenceMap parent) { super((AbstractReferenceMap) parent); } public V next() { return nextEntry().getValue(); } } /** * The MapIterator implementation. */ static class ReferenceMapIterator extends ReferenceBaseIterator implements MapIterator { protected ReferenceMapIterator(final AbstractReferenceMap parent) { super(parent); } public K next() { return nextEntry().getKey(); } public K getKey() { final HashEntry current = currentEntry(); if (current == null) { throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); } return current.getKey(); } public V getValue() { final HashEntry current = currentEntry(); if (current == null) { throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID); } return current.getValue(); } public V setValue(final V value) { final HashEntry current = currentEntry(); if (current == null) { throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID); } return current.setValue(value); } } //----------------------------------------------------------------------- // These two classes store the hashCode of the key of // of the mapping, so that after they're dequeued a quick // lookup of the bucket in the table can occur. /** * A soft reference holder. */ static class SoftRef extends SoftReference { /** the hashCode of the key (even if the reference points to a value) */ private final int hash; public SoftRef(final int hash, final T r, final ReferenceQueue q) { super(r, q); this.hash = hash; } @Override public int hashCode() { return hash; } } /** * A weak reference holder. */ static class WeakRef extends WeakReference { /** the hashCode of the key (even if the reference points to a value) */ private final int hash; public WeakRef(final int hash, final T r, final ReferenceQueue q) { super(r, q); this.hash = hash; } @Override public int hashCode() { return hash; } } //----------------------------------------------------------------------- /** * Replaces the superclass method to store the state of this class. *

* Serialization is not one of the JDK's nicest topics. Normal serialization will * initialise the superclass before the subclass. Sometimes however, this isn't * what you want, as in this case the put() method on read can be * affected by subclass state. *

* The solution adopted here is to serialize the state data of this class in * this protected method. This method must be called by the * writeObject() of the first serializable subclass. *

* Subclasses may override if they have a specific field that must be present * on read before this implementation will work. Generally, the read determines * what must be serialized here, if anything. * * @param out the output stream * @throws IOException if an error occurs while writing to the stream */ @Override protected void doWriteObject(final ObjectOutputStream out) throws IOException { out.writeInt(keyType.value); out.writeInt(valueType.value); out.writeBoolean(purgeValues); out.writeFloat(loadFactor); out.writeInt(data.length); for (final MapIterator it = mapIterator(); it.hasNext();) { out.writeObject(it.next()); out.writeObject(it.getValue()); } out.writeObject(null); // null terminate map // do not call super.doWriteObject() as code there doesn't work for reference map } /** * Replaces the superclass method to read the state of this class. *

* Serialization is not one of the JDK's nicest topics. Normal serialization will * initialise the superclass before the subclass. Sometimes however, this isn't * what you want, as in this case the put() method on read can be * affected by subclass state. *

* The solution adopted here is to deserialize the state data of this class in * this protected method. This method must be called by the * readObject() of the first serializable subclass. *

* Subclasses may override if the subclass has a specific field that must be present * before put() or calculateThreshold() will work correctly. * * @param in the input stream * @throws IOException if an error occurs while reading from the stream * @throws ClassNotFoundException if an object read from the stream can not be loaded */ @Override @SuppressWarnings("unchecked") protected void doReadObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { this.keyType = ReferenceStrength.resolve(in.readInt()); this.valueType = ReferenceStrength.resolve(in.readInt()); this.purgeValues = in.readBoolean(); this.loadFactor = in.readFloat(); final int capacity = in.readInt(); init(); data = new HashEntry[capacity]; while (true) { final K key = (K) in.readObject(); if (key == null) { break; } final V value = (V) in.readObject(); put(key, value); } threshold = calculateThreshold(data.length, loadFactor); // do not call super.doReadObject() as code there doesn't work for reference map } /** * Provided protected read-only access to the key type. * @param type the type to check against. * @return true if keyType has the specified type */ protected boolean isKeyType(ReferenceStrength type) { return this.keyType == type; } } -----