📄 emoticonfilter.java
字号:
/** Copyright (c) 2001 Sun Microsystems, Inc. All rights* reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:** 1. Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.** 2. Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following discalimer in* the documentation and/or other materials provided with the* distribution.** 3. The end-user documentation included with the redistribution,* if any, must include the following acknowledgment:* "This product includes software developed by the* Sun Microsystems, Inc. for Project JXTA."* Alternately, this acknowledgment may appear in the software itself,* if and wherever such third-party acknowledgments normally appear.** 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"* must not be used to endorse or promote products derived from this* software without prior written permission. For written* permission, please contact Project JXTA at http://www.jxta.org.** 5. Products derived from this software may not be called "JXTA",* nor may "JXTA" appear in their name, without prior written* permission of Sun.** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF* SUCH DAMAGE.* ====================================================================** This software consists of voluntary contributions made by many* individuals on behalf of Project JXTA. For more* information on Project JXTA, please see* <http://www.jxta.org/>.** This license is based on the BSD license adopted by the Apache Foundation.** $Id: EmoticonFilter.java,v 1.7 2007/05/28 22:00:52 nano Exp $*/package net.jxta.myjxta.dialog.filter;import net.jxta.logging.Logging;import net.jxta.myjxta.dialog.DialogMessage;import net.jxta.myjxta.dialog.DialogMessageWrapper;import net.jxta.myjxta.util.Constants;import net.jxta.myjxta.util.Resources;import java.io.*;import java.net.URL;import java.util.*;import java.util.logging.Level;import java.util.logging.Logger;import java.util.regex.Matcher;import java.util.regex.Pattern;import java.util.regex.PatternSyntaxException;/** * Substitues emoticon graphics for emoticon ascii sequences. * See net/jxta/myjxta/resources/themes/theme.txt for a list of emoticon sequences. * <pattern> * No substitution occurs if the emoticon sequence is preceeded by the escape character "/". * * @author james todd [gonzo at jxta dot org] * @author mike mcangus [mcangus at jxta dot org] * @version $Id: EmoticonFilter.java,v 1.7 2007/05/28 22:00:52 nano Exp $ */public final class EmoticonFilter extends AbstractDialogFilter { private static final Logger LOG = Logger.getLogger(EmoticonFilter.class.getName()); private static final String defaultTheme = "/net/jxta/myjxta/resources/themes/crystal-gaim/theme.txt"; private static final String DELIMITER = "/"; private static final String EMOTICON_ESCAPE_CHAR = "/"; private static final String ESCAPE_PATTERN_PATTERN = "\\"; private static final String PREFIX = "<img src=\""; private static final String POSTFIX = "\">"; // Small set of only the most often used HTML Character Entities private static final Set<String> CHAR_ENTITIES; static { CHAR_ENTITIES = new HashSet<String>(16); CHAR_ENTITIES.add("&"); CHAR_ENTITIES.add("©"); CHAR_ENTITIES.add(">"); CHAR_ENTITIES.add("<"); CHAR_ENTITIES.add(" "); CHAR_ENTITIES.add("""); CHAR_ENTITIES.add("®"); } private static final Map<String, List<Pattern>> patterns; // Loads the patterns map with emoticon graphic URL and a List of the emoticons for them. // Keeps the list in the same order as they appear in the theme.txt file. This allows us to ensure that emoticons // that are extensions of other emoticons ( e.g., >:O extends :O ) are always displayed properly if they appear // in the file before the emoticons that they extend. (clear?) static { Constants c = Constants.getInstance(); String theme = c.get(Constants.THEME_RESOURCES, defaultTheme); String base = theme.substring(0, theme.lastIndexOf(DELIMITER) + DELIMITER.length()); InputStream is = Resources.class.getResourceAsStream(theme); patterns = new LinkedHashMap<String, List<Pattern>>(); if (is != null) { StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(is))); st.ordinaryChars('0', '9'); st.wordChars('!', '~'); st.commentChar('#'); st.eolIsSignificant(true); try { String msgText = null; String t = null; String resource = null; boolean newLine = true; List<Pattern> emoticonList = null; Pattern pattern = null; while (st.nextToken() != StreamTokenizer.TT_EOF) { if (st.ttype == StreamTokenizer.TT_WORD) { msgText = st.sval; if (newLine) { newLine = false; resource = base + msgText; if (patterns.containsKey(resource)) { // if the same resource was previously defined in the themes file // we keep adding to its list of ascii emoticons. emoticonList = patterns.get(resource); } else { emoticonList = new ArrayList<Pattern>(); patterns.put(resource, emoticonList); } } else { t = escape(msgText); try { pattern = Pattern.compile(t); } catch (PatternSyntaxException pse) { LOG.log(Level.SEVERE, "Caught unexpected Exception", pse); continue; } if (!exists(pattern)) { emoticonList.add(pattern); } } } else if (st.ttype == StreamTokenizer.TT_NUMBER) { //ignore numbers} } else if (st.ttype == StreamTokenizer.TT_EOL) { newLine = true; } } } catch (IOException ioe) { LOG.log(Level.SEVERE, "Caught unexpected Exception", ioe); } } if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Map:patterns = ["); for (Iterator<String> pit = patterns.keySet().iterator(); pit.hasNext();) { for (Iterator lit = patterns.get(pit.next()).iterator(); lit.hasNext();) { LOG.fine(" " + ((Pattern) lit.next()).pattern()); } } LOG.fine(" ]"); } } /** * Finds emoticon sequences in the message text, and substitutes the URL of the corresponding image. * Uses {@link java.util.regex.Pattern regex Pattern} and {@link java.util.regex.Matcher regex Matcher} * to find emoticon sequences. * <p/> * No substitution occurs if the emoticon sequence is preceeded by the escape character "/". * <p/> * Also, certain HTML Character Entities are check for to ensure that if they occur near a paren * that they don't cause extraneous emoticon images to be generated. * * @param msg the newly received DialogMessage object * @return the newly modified DialogMessage object. * @see net.jxta.myjxta.dialog.DialogFilter#filter(net.jxta.myjxta.dialog.DialogMessage) */ public DialogMessage filter(DialogMessage msg) { if (!processMessage((DialogMessageWrapper) msg)) { return msg; } String msgText = msg.getHtmlMessage() != null ? msg.getHtmlMessage().trim() : ""; LOG.fine("Begin filter(DialogMessage)"); LOG.fine("msgText = " + msgText); if (msgText != null && msgText.length() > 0) { Pattern pattern = null; List patternList = null; Matcher matcher = null; String resource = null; URL emoticonUrl = null; StringBuffer workingBuff = new StringBuffer(); for (Iterator<String> resources = patterns.keySet().iterator(); resources.hasNext();) { resource = resources.next(); patternList = patterns.get(resource); for (Iterator patternIt = patternList.iterator(); patternIt.hasNext();) { pattern = (Pattern) patternIt.next(); matcher = pattern.matcher(msgText); if (matcher.find()) { String match = matcher.group(); String[] nonMatchingText = pattern.split(msgText); if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Found matching text \"" + match + "\" for pattern \"" + pattern.pattern() + "\""); StringBuffer nmtString = new StringBuffer("[").append(Constants.CRLF).append("\"").append(nonMatchingText[0]).append("\""); for (int i = 1; i < nonMatchingText.length; i++) { nmtString.append(",").append(Constants.CRLF).append("\"").append(nonMatchingText[i]).append("\""); } nmtString.append(Constants.CRLF).append("]"); LOG.fine("nonMatchingText = " + nmtString.toString()); LOG.fine("nonMatchingText.length = " + nonMatchingText.length); } emoticonUrl = Resources.class.getResource(resource); String imageTag = PREFIX + emoticonUrl + POSTFIX; int textIndex = 0; while (textIndex < nonMatchingText.length - 1) { if (nonMatchingText[textIndex].endsWith(EMOTICON_ESCAPE_CHAR)) { LOG.fine("Escaping emoticon"); workingBuff.append(nonMatchingText[textIndex].substring(0, nonMatchingText[textIndex].length() - 1)); workingBuff.append(match); } else { int ampX = nonMatchingText[textIndex].lastIndexOf('&'); if (match.startsWith(";") && ampX >= 0) { String testEntityChar = nonMatchingText[textIndex].substring(ampX); if (CHAR_ENTITIES.contains(testEntityChar)) { LOG.fine("Character Entity, not real emoticon"); workingBuff.append(nonMatchingText[textIndex]); workingBuff.append(match); } else { LOG.fine("Substituting emoticon"); workingBuff.append(nonMatchingText[textIndex]).append(imageTag).append(" "); } } else { LOG.fine("Substituting emoticon"); workingBuff.append(nonMatchingText[textIndex]).append(imageTag).append(" "); } } textIndex++; } workingBuff.append(nonMatchingText[textIndex]); // update the msgText and reset the working text for the next emoticon msgText = workingBuff.toString(); workingBuff.setLength(0); } } } msg.setHtmlMessage(msgText); } if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("returning: " + msgText); LOG.fine("End filter(DialogMessage)"); } return msg; } private static boolean exists(Pattern msgPat) { for (String key : patterns.keySet()) { Set<Pattern> patternSet = new HashSet<Pattern>(patterns.get(key)); if (patternSet.contains(msgPat)) { return true; } } return false; } private static String escape(String s) { if (s == null) { return null; } char[] c = s.toCharArray(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < c.length; i++) { if (!Character.isLetterOrDigit(c[i])) { sb.append(ESCAPE_PATTERN_PATTERN); } sb.append(c[i]); }// return c != null ? (ESCAPE_EMOTICON_PATTERN + "(" + sb.toString() + ")") : s;// return c != null ? (ESCAPE_EMOTICON_PATTERN + "(" + CHAR_ENTITIES_PATTERN + sb.toString() + ")") : s; return sb.toString(); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -