/* * (C) Copyright 2015 by fr3ts0n <erwin.scheuch-heilig@gmx.at> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package com.fr3ts0n.prot; import org.apache.log4j.Logger; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.DecimalFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.Vector; /** * Generic Base Class of Protocol header hadling * * @author $Author: erwin $ */ public abstract class ProtoHeader extends TelegramSender implements TelegramListener, TelegramWriter { /** Logging object */ protected static Logger log = Logger.getLogger("com.fr3ts0n.prot"); /** List of telegram listeners */ @SuppressWarnings("rawtypes") protected Vector TelegramListeners = new Vector(); /** object to be used for parsing and formatting timestamps */ protected static SimpleDateFormat TimeStampFormat = new SimpleDateFormat("yyyyMMddHHmmssSS"); /** object to be used for parsing and formatting datestamps */ protected static SimpleDateFormat DateStampFormat = new SimpleDateFormat("yyyyMMdd"); /** object to be used for parsing and formatting decimal numbers */ protected static DecimalFormat decimalFormat = new DecimalFormat("0000000000"); /** empty buffer definition for further usage * */ protected static final char emptyBuffer[] = {}; /** * Parameter type definitions */ /** alphanumeric parameter (String) */ public static final int PT_ALPHA = 0; /** numeric integer parameter */ public static final int PT_NUMERIC = 1; /** numeric float parameter */ public static final int PT_FLOAT = 2; /** TimeStamp parameter */ public static final int PT_TIMESTAMP = 3; /** DateStamp parameter */ public static final int PT_DATESTAMP = 4; /** Integer parameter */ public static final int PT_INTEGER = 5; /** BCD parameter */ public static final int PT_BCD = 6; /** HEX parameter */ public static final int PT_HEX = 7; /** HEX parameter signed 8 Bit */ public static final int PT_HEX_S8 = 8; /** HEX parameter signed 16 Bit */ public static final int PT_HEX_S16 = 9; /** String Value within hex parameter (HEXDUMP) */ public static final int PT_HEX_ALPHA = 10; /** * Indices of Parameter definitions (int[index]) */ /** start position within telegram (staring with 1) */ static final int ID_START = 0; /** length of parameter within telegram (staring with 1) */ static final int ID_LEN = 1; /** type of parameter see type PT_xxxx */ static final int ID_TYPE = 2; /** string padding character */ protected static char paddingChr = ' '; /** check if telegram listeners have to be notified on empty payload */ protected boolean isNotificationOnEmptyPayloadAllowed = false; /** * list of parameters for specific protocol * * @return complete set of protocol parameters */ public abstract int[][] getTelegramParams(); /** * list of parameter descriptions for specific protocol * * @return complete set of protocol parameter description strings */ protected abstract String[] getParamDescriptors(); /** * return message footer for protocol payload * * @param buffer buffer of payload data * @return buffer of message footer */ public abstract char[] getFooter(char[] buffer); /** * create a new telegram header for selected payload data buffer * inclunding setting all ID's, sizes and validity issues * * @param buffer buffer of payload data * @return buffer of new telegram header */ protected abstract char[] getNewHeader(char[] buffer); /** * create a new telegram header inclunding setting all ID's, sizes and * validity issues * * @return buffer of new telegram header */ protected abstract char[] getNewHeader(char[] buffer, int type, Object id); /** * create a new telegram header inclunding setting all ID's, sizes and * validity issues (empty payload) * * @return buffer of new telegram header */ protected char[] getNewHeader() { return getNewHeader(emptyBuffer); } /** * return message footer for protocol * * @return length of message footer */ public int getFooterLength() { return (getFooter(emptyBuffer).length); } /** * return specific record for telegram parameter * * @return one specific record for telegram parameter */ public int[] getParamEntry(int index, int[][] paramMap) { return (paramMap[index]); } /** * return specific record for telegram parameter * * @return one specific record for telegram parameter */ public int[] getTelegramParam(int index) { return (getTelegramParams()[index]); } /** * return length of one message header * * @return length of message header */ public static int getBufferLength(int[][] fieldMap) { int len = 0; for (int cnt = 0; cnt < fieldMap.length; cnt++) { len += fieldMap[cnt][ID_LEN]; } return (len); } /** * return length of one message header * * @return length of message header */ public int getHeaderLength() { return (getBufferLength(getTelegramParams())); } /** * return message parameter as String * * @param start start offset in buffer * @param len length in bytes * @param buffer telegram buffer to be read * @return parameter value as String */ public static String getParamString(int start, int len, char[] buffer) { String result = new String(buffer, start, len); return (result); } /** * return message parameter as String * * @param id = ID of telegram parameter * @param fieldMap map of field parameters * @param buffer telegram buffer to be read * @return parameter value as String */ public static String getParamString(int id, int[][] fieldMap, char[] buffer) { int currParam[] = fieldMap[id]; return (getParamString(currParam[ID_START], currParam[ID_LEN], buffer)); } /** * return message parameter as String * * @param id = ID of telegram parameter * @param buffer telegram buffer to be read * @return parameter value as String */ public String getParamString(int id, char[] buffer) { return (getParamString(id, getTelegramParams(), buffer)); } /** * return value of message parameter as Integer (binary) * Object with corresponding data type * * @param start start offset in buffer * @param len length in bytes * @param buffer telegram buffer to be read * @return parameter value as Object */ public static Integer getParamInt(int start, int len, char[] buffer) { int ofs; int value = 0; // if specified length is 0, take all the rest if(len == 0) len = buffer.length - start; for (ofs = start; ofs < start + len; ofs++) { value <<= 8; value |= buffer[ofs]; } return Integer.valueOf(value); } /** * return value of message parameter as Integer (binary) * Object with corresponding data type * * @param id = ID of telegram parameter * @param fieldMap map of field parameters * @param buffer telegram buffer to be read * @return parameter value as Object */ public static Integer getParamInt(int id, int[][] fieldMap, char[] buffer) { int currParam[] = fieldMap[id]; return (getParamInt(currParam[ID_START], currParam[ID_LEN], buffer)); } /** * return value of message parameter as Integer (binary) * Object with corresponding data type * * @param id = ID of telegram parameter * @param buffer telegram buffer to be read * @return parameter value as Object */ public Integer getParamInt(int id, char[] buffer) { return (getParamInt(id, getTelegramParams(), buffer)); } /** * return value of message parameter as Integer (binary) * from BCD Data type * * @param start start offset in buffer * @param len length in bytes * @param buffer telegram buffer to be read * @return parameter value as Object */ public static Integer getParamBCD(int start, int len, char[] buffer) { int ofs; int value = 0; int factor = 1; for (ofs = 0; ofs < len; ofs++) { value += Integer.valueOf(Integer.toHexString(buffer[start + len - ofs]), 16).intValue() * factor; factor *= 0x100; } return Integer.valueOf(value); } /** * return value of message parameter as Integer (binary) * from BCD Data type * * @param id = ID of telegram parameter * @param fieldMap map of field parameters * @return parameter value as Object */ public static Integer getParamBCD(int id, int[][] fieldMap, char[] buffer) { int currParam[] = fieldMap[id]; return (getParamBCD(currParam[ID_START], currParam[ID_LEN], buffer)); } /** * return value of message parameter as Integer (binary) * from BCD Data type * * @param id = ID of telegram parameter * @param buffer message buffer * @return parameter value as Object */ public Integer getParamBCD(int id, char[] buffer) { return (getParamBCD(id, getTelegramParams(), buffer)); } /** * return value of message parameter as String from HEX-Dump * * @param start start offset in buffer * @param len length in bytes * @param buffer telegram buffer to be read * @return parameter value as Object */ public static String getParamHexAlpha(int start, int len, char[] buffer) { int ofs; String msgStr = new String(buffer); StringBuffer result = new StringBuffer(); for (ofs = 0; ofs < len; ofs += 2) { result.append(Integer.valueOf(msgStr.substring(start + ofs, start + ofs + 2), 16).intValue()); } return (result.toString()); } /** * return value of message parameter as String from HEX-Dump * * @param id = ID of telegram parameter * @param fieldMap map of field parameters * @param buffer message buffer * @return parameter value as Object */ public static String getParamHexAlpha(int id, int[][] fieldMap, char[] buffer) { int currParam[] = fieldMap[id]; return (getParamHexAlpha(currParam[ID_START], currParam[ID_LEN], buffer)); } /** * return value of message parameter as String from HEX-Dump * * @param id = ID of telegram parameter * @param buffer message buffer * @return parameter value as Object */ public String getParamHexAlpha(int id, char[] buffer) { return (getParamHexAlpha(id, getTelegramParams(), buffer)); } /** * return value of message parameter as * Object with corresponding data type * * @param id = ID of telegram parameter * @param fieldMap map of field parameters * @param buffer message buffer * @return parameter value as Object */ public static Object getParamValue(int id, int[][] fieldMap, char[] buffer) { Object result = null; int currParam[] = fieldMap[id]; int tmp; switch (currParam[ID_TYPE]) { case PT_ALPHA: result = getParamString(id, fieldMap, buffer); if (result == null) { result = new String(); } break; case PT_NUMERIC: result = Long.valueOf(getParamString(id, fieldMap, buffer).trim()); break; case PT_FLOAT: result = Double.valueOf(getParamString(id, fieldMap, buffer).trim()); break; case PT_TIMESTAMP: try { result = TimeStampFormat.parse(getParamString(id, fieldMap, buffer)); } catch (ParseException ex) { ex.printStackTrace(); } break; case PT_DATESTAMP: try { result = DateStampFormat.parse(getParamString(id, fieldMap, buffer)); } catch (ParseException ex) { ex.printStackTrace(); } break; case PT_INTEGER: result = getParamInt(id, fieldMap, buffer); break; case PT_BCD: result = getParamBCD(id, fieldMap, buffer); break; case PT_HEX: result = Integer.valueOf(getParamString(id, fieldMap, buffer), 16); break; case PT_HEX_S8: tmp = Integer.valueOf(getParamString(id, fieldMap, buffer), 16).intValue(); result = Integer.valueOf((tmp & 0x80) == 0 ? tmp : tmp - 0x100); break; case PT_HEX_S16: tmp = Integer.valueOf(getParamString(id, fieldMap, buffer), 16).intValue(); result = Integer.valueOf((tmp & 0x8000) == 0 ? tmp : tmp - 0x10000); break; case PT_HEX_ALPHA: result = getParamHexAlpha(id, fieldMap, buffer); break; default: log.error("Invalid Parameter Type" + String.valueOf(currParam[ID_TYPE])); break; } return (result); } /** * return value of message parameter as * Object with corresponding data type * * @return parameter value as Object */ public Object getParamValue(int id, char[] buffer) { return (getParamValue(id, getTelegramParams(), buffer)); } /** * return type of telegram parameter * * @param id = ID of telegram parameter * @param fieldMap map of field parameters * @return Class type of message parameter */ @SuppressWarnings("rawtypes") public Class getParamType(int id, int[][] fieldMap) { Class result = null; int currParam[] = fieldMap[id]; switch (currParam[ID_TYPE]) { case PT_ALPHA: case PT_HEX_ALPHA: result = String.class; break; case PT_NUMERIC: result = Long.class; break; case PT_FLOAT: result = Double.class; break; case PT_TIMESTAMP: case PT_DATESTAMP: result = Date.class; break; case PT_INTEGER: case PT_BCD: case PT_HEX: case PT_HEX_S8: case PT_HEX_S16: result = Integer.class; break; default: log.error("Invalid Parameter Type" + String.valueOf(currParam[ID_TYPE])); break; } return (result); } /** * return type of telegram parameter * * @param id = ID of telegram parameter * @return Class type of message parameter */ @SuppressWarnings("rawtypes") public Class getParamType(int id) { return (getParamType(id, getTelegramParams())); } /** * set telegram parameter <pre>id</pre> to content of string * * @param id = ID of telegram parameter * @param fieldMap map of field parameters * @param buffer = current telegram buffer * @param start = start offset * @param value = String value of new parameter * @return curent telegram buffer */ public static char[] setParamString(int id, int[][] fieldMap, char[] buffer, int start, String value) { // find out desired parameter int currParam[] = fieldMap[id]; char valueChars[] = value.toCharArray(); // set parameter within buffer for (int cnt = 0; cnt < currParam[ID_LEN]; cnt++) { // depending on position within parameter ... if (cnt < start) // ... pad parameter with leading spaces { buffer[currParam[ID_START] + cnt] = (char) paddingChr; } else if (cnt - start < valueChars.length) // ... set parameter char { buffer[currParam[ID_START] + cnt] = (char) valueChars[cnt - start]; } else // ... set terinating 0's { buffer[currParam[ID_START] + cnt] = (char) paddingChr; } } // and return the buffer again return (buffer); } /** * set telegram parameter <pre>id</pre> to content of string * * @param id = ID of telegram parameter * @param buffer = current telegram buffer * @param start = start offset * @param value = String value of new parameter * @return curent telegram buffer */ public char[] setParamString(int id, char[] buffer, int start, String value) { return (setParamString(id, getTelegramParams(), buffer, start, value)); } /** * set telegram parameter <pre>id</pre> to content of string * * @param id = ID of telegram parameter * @param buffer = current telegram buffer * @param value = String value of new parameter * @return curent telegram buffer */ public char[] setParamString(int id, char[] buffer, String value) { return (setParamString(id, buffer, 0, value)); } /** * set telegram parameter <pre>id</pre> as HexDump to content of string * * @param id = ID of telegram parameter * @param fieldMap map of field parameters * @param buffer = current telegram buffer * @param start = start offset * @param value = String value of new parameter * @return curent telegram buffer */ public static char[] setParamHexAlpha(int id, int[][] fieldMap, char[] buffer, int start, String value) { String hexStr = ""; char valChars[] = value.toCharArray(); // create hex format of string for (int i = 0; i < value.length(); i++) { hexStr += String.format("%02X", valChars[i]); } // .. and set it as parameter return (setParamString(id, fieldMap, buffer, start, hexStr)); } /** * set telegram parameter <pre>id</pre> to BCD content of int * * @param id = ID of telegram parameter * @param fieldMap map of field parameters * @param buffer = current telegram buffer * @param value = String value of new parameter * @return curent telegram buffer */ public static char[] setParamBCD(int id, int[][] fieldMap, char[] buffer, int value) { int currParam[] = fieldMap[id]; int ofs; int rest = value; for (ofs = currParam[ID_LEN] - 1; ofs >= 0; ofs--) { buffer[currParam[ID_START] + ofs] = (char) Integer.valueOf(String.valueOf(rest % 100), 16).intValue(); rest = rest / 100; } return (buffer); } /** * set telegram parameter <pre>id</pre> to BCD content of int * * @param id = ID of telegram parameter * @param buffer = current telegram buffer * @param value = String value of new parameter * @return curent telegram buffer */ public char[] setParamBCD(int id, char[] buffer, int value) { return (setParamBCD(id, getTelegramParams(), buffer, value)); } /** * set telegram parameter <pre>id</pre> to Integer content of int * * @param id = ID of telegram parameter * @param fieldMap map of field parameters * @param buffer = current telegram buffer * @param value = value of new parameter * @return curent telegram buffer */ public static char[] setParamInt(int id, int[][] fieldMap, char[] buffer, int value) { int currParam[] = fieldMap[id]; int ofs; int rest = value; for (ofs = currParam[ID_LEN] - 1; ofs >= 0; ofs--) { buffer[currParam[ID_START] + ofs] = (char) (rest % 0x100); rest = rest / 0x100; } return (buffer); } /** * set telegram parameter <pre>id</pre> to Integer content of int * * @param id = ID of telegram parameter * @param buffer = current telegram buffer * @param value = value of new parameter * @return curent telegram buffer */ public char[] setParamInt(int id, char[] buffer, int value) { return (setParamInt(id, getTelegramParams(), buffer, value)); } /** * set telegram parameter <pre>id</pre> to content of object * * @param id = ID of telegram parameter * @param fieldMap map of field parameters * @param buffer = current telegram buffer * @param value = String value of new parameter * @return curent telegram buffer */ public static char[] setParamValue(int id, int[][] fieldMap, char[] buffer, Object value) { String parStr; char result[] = {}; int currParam[] = fieldMap[id]; // set parameter format based on it's type switch (currParam[ID_TYPE]) { case PT_ALPHA: // string parameters are set left justified with trailing zeros result = setParamString(id, fieldMap, buffer, 0, value.toString()); break; case PT_HEX_ALPHA: // string parameters are set left justified with trailing zeros result = setParamHexAlpha(id, fieldMap, buffer, 0, value.toString()); break; case PT_NUMERIC: case PT_FLOAT: // numeric parameters are right justified padded with leadingspaces parStr = decimalFormat.format(value); result = setParamString(id, fieldMap, buffer, currParam[ID_LEN] - parStr.length(), parStr); break; case PT_HEX: case PT_HEX_S16: case PT_HEX_S8: // numeric parameters are right justified padded with leading zeros parStr = Integer.toHexString(((Integer) value).intValue()).toUpperCase(); result = setParamString(id, fieldMap, buffer, currParam[ID_LEN] - parStr.length(), parStr); break; case PT_TIMESTAMP: // timestamp parameters are set left justified with trailing zeros result = setParamString(id, fieldMap, buffer, 0, TimeStampFormat.format((Date) value)); break; case PT_DATESTAMP: // datestamp parameters are set left justified with trailing zeros result = setParamString(id, fieldMap, buffer, 0, DateStampFormat.format((Date) value)); break; case PT_INTEGER: result = setParamInt(id, fieldMap, buffer, ((Integer) value).intValue()); break; case PT_BCD: result = setParamBCD(id, fieldMap, buffer, ((Integer) value).intValue()); break; default: log.error("Invalid Parameter Type" + String.valueOf(currParam[ID_TYPE])); break; } return (result); } /** * set telegram parameter <pre>id</pre> to content of object * * @param id = ID of telegram parameter * @param buffer = current telegram buffer * @param value = String value of new parameter * @return curent telegram buffer */ public char[] setParamValue(int id, char[] buffer, Object value) { return (setParamValue(id, getTelegramParams(), buffer, value)); } /** * check telegram if it meets protocol requirements * current implementation only performs a size check, any other check * needs to be implemented in sub-class * * @param buffer = current telegram buffer * @return true if telegram is OK, otherwise false */ public boolean checkTelegram(char[] buffer) { return (buffer.length >= (getHeaderLength() + getFooterLength())); } /** * return the payload (user data) of telegram * * @param buffer = current telegram buffer * @return buffer of payload data */ public char[] getPayLoad(char[] buffer) { String result = ""; if (checkTelegram(buffer)) { result = new String(buffer, getHeaderLength(), buffer.length - getHeaderLength() - getFooterLength()); } return (result.toCharArray()); } /** * return String from HEX-Dump * * @param msgStr HEX-Dump of string * @return ASCII string of hex dump */ public static String hexStrToAlphaStr(String msgStr) { StringBuffer result = new StringBuffer(); for (int ofs = 0; ofs < msgStr.length(); ofs += 2) { result.append(String.format("%c", Integer.parseInt(msgStr.substring(ofs, ofs + 2), 16))); } return (result.toString()); } /** * return HEX-Dump from String * * @param msgStr ASCII string to hex dump * @return HEX-Dump of string */ public static String alphaStrToHexStr(String msgStr) { StringBuffer result = new StringBuffer(); for (int ofs = 0; ofs < msgStr.length(); ofs++) { result.append(String.format("%02x", msgStr.charAt(ofs))); } return (result.toString()); } /** * dump message parameters of message buffer * * @param buffer = current telegram buffer */ public void dumpParameters(char[] buffer) { if (checkTelegram(buffer)) { for (int cnt = 0; cnt < getTelegramParams().length; cnt++) { try { log.debug(this.toString() + " " + getParamDescriptors()[cnt] + ": " + getParamInt(cnt, buffer)); } catch (Exception e) { // we don't want to do anything here ... } } log.debug(this.toString() + " Payload : " + ProtUtils.hexDumpBuffer(getPayLoad(buffer))); } else { log.error(this.toString() + " Invalid Telegram: '" + new String(buffer) + "' " + String.valueOf(buffer.length - getHeaderLength())); } } /** * Implementation of TelegramListener */ /** * handle incoming protocol telegram * default implementaion only checks telegram and notifies listeners with * protocol payload * * @param buffer - telegram buffer * @return number of listeners notified */ public int handleTelegram(char[] buffer) { int cnt = 0; log.debug(this.toString() + " RX:" + ProtUtils.hexDumpBuffer(buffer)); if (log.isDebugEnabled()) { dumpParameters(buffer); } // if telegram is OK if (checkTelegram(buffer)) { // then notify all listeners with telegram payload notifyTelegram(getPayLoad(buffer)); cnt++; } return (cnt); } /** * create an empy telegram header * * @return buffer of empty telegram header */ public static char[] createEmptyBuffer(int[][] fieldMap, char fillChar) { // create buffer char buffer[] = new char[getBufferLength(fieldMap)]; // fill buffer with blanks java.util.Arrays.fill(buffer, (char) fillChar); // and return it return (buffer); } /** * create an empy telegram header * * @return buffer of empty telegram header */ protected char[] createEmptyHeader() { // return newly created buffer return (createEmptyBuffer(getTelegramParams(), ' ')); } /** * create a new protocol telegram containing specified payload * * @param payLoad user data buffer to be packed into protocol telegram * @return buffer of packet telegram */ public char[] createTelegram(char[] payLoad) { String tgmStr = new String(getNewHeader(payLoad)) + new String(payLoad) + new String(getFooter(payLoad)); return (tgmStr.toCharArray()); } /** * create a new protocol telegram containing specified payload * * @param payLoad user data buffer to be packed into protocol telegram * @return buffer of packet telegram */ public char[] createTelegram(char[] payLoad, int type, Object id) { char[] result = {}; String tgmStr = new String(getNewHeader(payLoad, type, id)) + new String(payLoad) + new String(getFooter(payLoad)); result = tgmStr.toCharArray(); return result; } /** * Handlers for TelegramListener List */ /** * add a new listener to be notified about new telegrams * * @param newListener - TelegramListener to be added * @return true if adding was OK, otherwise false */ @SuppressWarnings("unchecked") public boolean addTelegramListener(TelegramListener newListener) { return (TelegramListeners.add(newListener)); } /** * remove a listener to be notified about new telegrams * * @param remListener - TelegramListener to be removed * @return true if adding was OK, otherwise false */ public boolean removeTelegramListener(TelegramListener remListener) { return (TelegramListeners.remove(remListener)); } /** * Notify all telegram listeners about new telegram * * @param buffer - telegram buffer */ @SuppressWarnings("rawtypes") protected void notifyTelegram(char[] buffer) { // if there is anything to be notified about if (buffer.length > 0 || isNotificationOnEmptyPayloadAllowed) { // loop through all listeners ... Iterator it = TelegramListeners.iterator(); Object currListener; while (it.hasNext()) { currListener = it.next(); if (currListener != null && currListener instanceof TelegramListener) { // ... and tell them about it ((TelegramListener) currListener).handleTelegram(buffer); } } } } /** * convert hexadecimal string to array of bytes * each byte is represented by 2 characters in hex string * * @param hexString hexadecomal string as responded by ELM adapter * @return byte array representing the content of hex string */ public static char[] hexToBytes(String hexString) { char[] result = new char[hexString.length() / 2]; for (int i = 0; i < hexString.length(); i += 2) { result[i / 2] = (char) Integer.parseInt(hexString.substring(i, i + 2), 16); } return (result); } /** * * Implementation of Interface TelegramWriter * */ /** * handle outgoing protocol telegram * * @param buffer - telegram buffer * @return number of bytes sent */ public int writeTelegram(char[] buffer) { sendTelegram(createTelegram(buffer)); return (buffer.length); } /** * handle outgoing protocol telegram * * @param buffer - telegram buffer * @return number of bytes sent */ public int writeTelegram(char[] buffer, int type, Object id) { sendTelegram(createTelegram(buffer, type, id)); return (buffer.length); } /** Utility field used by event firing mechanism. */ private Vector<PropertyChangeListener> listenerList = null; /** * Registers PropertyChangeListener to receive events. * * @param listener The listener to register. */ public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { if (listenerList == null) { listenerList = new Vector<PropertyChangeListener>(); } listenerList.add(listener); } /** * Removes PropertyChangeListener from the list of listeners. * * @param listener The listener to remove. */ public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { listenerList.remove(listener); } /** * Notifies all registered listeners about the event. * * @param event The event to be fired */ protected void firePropertyChange(PropertyChangeEvent event) { if (listenerList == null) return; PropertyChangeListener listener; Iterator<PropertyChangeListener> it = listenerList.iterator(); while (it.hasNext()) { listener = it.next(); listener.propertyChange(event); } } }