Java tutorial
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved. Portions Copyrighted 2012 Daniel Huss. * * The contents of this file are subject to the terms of either the GNU General Public License Version 2 only ("GPL") or * the Common Development and Distribution License("CDDL") (collectively, the "License"). You may not use this file * except in compliance with the License. You can obtain a copy of the License at * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the specific language * governing permissions and limitations under the License. When distributing the software, include this License Header * Notice in each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this particular * file as subject to the "Classpath" exception as provided by Sun in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the License Header, with the fields enclosed by * brackets [] replaced by your own identifying information: "Portions Copyrighted [year] [name of copyright owner]" * * Contributor(s): * * The Original Software is NetBeans. The Initial Developer of the Original Software is Sun Microsystems, Inc. Portions * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. * * If you wish your version of this file to be governed by only the CDDL or only the GPL Version 2, indicate your * decision by adding "[Contributor] elects to include this software in this distribution under the [CDDL or GPL Version * 2] license." If you do not indicate a single choice of license, a recipient has the option to distribute your version * of this file under either the CDDL, the GPL Version 2 or to extend the choice of license to its licensees as provided * above. However, if you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then the option * applies only if the new code is made subject to such option by the copyright holder. */ package de.unentscheidbar.validation.swing.trigger; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Objects; import javax.annotation.Nullable; import javax.swing.JComponent; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import org.apache.commons.lang3.ClassUtils; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.SetMultimap; import de.unentscheidbar.validation.internal.Beans; import de.unentscheidbar.validation.swing.ImmutableFunction; final class DocumentChangeTrigger extends AbstractCoalescingTrigger<JComponent> implements DocumentListener, PropertyChangeListener { private static final class DocumentGetter extends ImmutableFunction<JComponent, Document> { static final DocumentGetter INSTANCE = new DocumentGetter(); @Override @Nullable public Document apply(@Nullable JComponent input) { if (input == null) return null; if (input instanceof JTextComponent) { return ((JTextComponent) input).getDocument(); } else { return Beans.propertyValue(input, DOCUMENT_PROPERTY); } } } public static final Trigger<JComponent> INSTANCE = new DocumentChangeTrigger(); static final String DOCUMENT_PROPERTY = "document"; /* Knows which document belongs to which components */ private final SetMultimap<Wrapper<Document>, Wrapper<JComponent>> documentMap = LinkedHashMultimap.create(); @Override public void changedUpdate(DocumentEvent e) { notify(e); } @Override public void insertUpdate(DocumentEvent e) { notify(e); } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getSource() instanceof JComponent) { JComponent src = (JComponent) evt.getSource(); forgetDocument((Document) evt.getOldValue(), src); observeDocument(src); } else { /* JComponent correctly reports itself as the event source, so this should never happen */ throw new IllegalStateException("Received a PropertyChangeEvent with unexpected source type: " + ClassUtils.getSimpleName(evt.getSource(), "NULL")); } } @Override public void registerSelf(JComponent component) { Objects.requireNonNull(component, "component"); component.addPropertyChangeListener(DOCUMENT_PROPERTY, this); observeDocument(component); } @Override public void removeUpdate(DocumentEvent e) { notify(e); } @Override public void unregisterSelf(JComponent component) { forgetDocument(DocumentGetter.INSTANCE.apply(component), component); } private void forgetDocument(Document doc, JComponent component) { if (doc == null) { return; } Wrapper<Document> docIdentity = Beans.identity(doc); documentMap.remove(docIdentity, Beans.identity(component)); if (!documentMap.containsKey(docIdentity)) { doc.removeDocumentListener(this); } } private void notify(DocumentEvent e) { /* Multiple components sharing a document instance is rare but we've got it covered */ for (Wrapper<JComponent> component : documentMap.get(Beans.identity(e.getDocument()))) { fireEvent(component.get()); } } private void observeDocument(JComponent component) { Document doc = DocumentGetter.INSTANCE.apply(component); Wrapper<Document> docIdentity = Beans.identity(doc); Wrapper<JComponent> componentIdentity = Beans.identity(component); /* Don't listen more than once to each document */ documentMap.remove(docIdentity, componentIdentity); if (!documentMap.containsKey(docIdentity)) { doc.removeDocumentListener(this); doc.addDocumentListener(this); } documentMap.put(docIdentity, componentIdentity); } public static boolean supports(Object classOrInstance) { if (classOrInstance instanceof Class<?>) { return supportsClass((Class<?>) classOrInstance); } else { return supportsInstance(classOrInstance); } } private static boolean supportsInstance(Object instance) { return (instance instanceof JTextComponent) || supportsClass(instance.getClass()); } private static boolean supportsClass(Class<?> clazz) { return ClassUtils.isAssignable(clazz, JTextComponent.class) || Beans.hasReadableProperty(clazz, DOCUMENT_PROPERTY, Document.class); } }