001package jmri.jmrix.loconet.sdf; 002 003import java.util.ArrayList; 004import java.util.List; 005 006/** 007 * Common base for all the SDF macros defined by Digitrax for their sound 008 * definition language. 009 * <p> 010 * Each macro has a number of descriptive forms: 011 * <dl> 012 * <dt>name()<dd>Just the name, in MPASM form. 013 * <dt>toString()<dd>A brief description, with a terminating newline 014 * <dt>oneInstructionString()<dd>The entire single instruction in MPASM from, 015 * with a terminating newline 016 * <dt>allInstructionString()<dd>The instruction and all those logically grouped 017 * within it. 018 * </dl> 019 * 020 * SdfMacro and its subclasses don't do the notification needed to be Models in 021 * an MVC edit paradyme. This is because there are a lot of SdfMacros in 022 * realistic sound file, and the per-object overhead needed would be too large. 023 * Hence (or perhaps because of no need), there is no support for simultaneous 024 * editing of a single macro instruction updating multiple windows. You can have 025 * multiple editors open on a single SdfBuffer, but these are not interlocked 026 * against each other. (We could fix this by having a shared pool of "objects to 027 * be notified of changes in the SdfBuffer, acccessed by reference during 028 * editing (to avoid another dependency), but that's a project for another day) 029 * 030 * @author Bob Jacobsen Copyright (C) 2007 031 */ 032public abstract class SdfMacro implements SdfConstants { 033 034 /** 035 * Name used by the macro in the SDF definition 036 * 037 * @return Fixed name associated with this type of instructio 038 */ 039 abstract public String name(); 040 041 /** 042 * Provide number of bytes defined by this macro 043 * 044 * @return Fixed numher of bytes defined (a constant for the instruction 045 * type) 046 */ 047 abstract public int length(); 048 049 /** 050 * Provide a single-line simplified representation, including the trailing 051 * newline. This is used e.g. in the tree format section of the 052 * {@link jmri.jmrix.loconet.sdfeditor.EditorFrame}. 053 * 054 * @return newline-terminated string; never null 055 */ 056 @Override 057 abstract public String toString(); 058 059 /** 060 * Provide single instruction in MPASM format, including the trailing 061 * newline. 062 * 063 * @return Newline terminated string, never null 064 */ 065 abstract public String oneInstructionString(); 066 067 /** 068 * Provide instructions in MPASM format, including the trailing newline and 069 * all nested instructions. 070 * 071 * @return Newline terminated string, never null 072 * @param indent String inserted at the start of each output line, typically 073 * some number of spaces. 074 */ 075 abstract public String allInstructionString(String indent); 076 077 /** 078 * Access child (nested) instructions. 079 * 080 * @return List of children, which will be null except in case of nesting. 081 */ 082 public List<SdfMacro> getChildren() { 083 return children; 084 } 085 /** 086 * Local member hold list of child (contained) instructions 087 */ 088 ArrayList<SdfMacro> children = null; // not changed unless there are some! 089 090 /** 091 * Total length, including contained instructions 092 * 093 * @return length of all parts 094 */ 095 public int totalLength() { 096 int result = length(); 097 List<SdfMacro> l = getChildren(); 098 if (l == null) { 099 return result; 100 } 101 for (int i = 0; i < l.size(); i++) { 102 result += l.get(i).totalLength(); 103 } 104 return result; 105 } 106 107 /** 108 * Store into a buffer. 109 * <p> 110 * This provides a default implementation for children, but each subclass 111 * needs to store its own data with setAtIndexAndInc(). 112 * 113 * @param buffer load with all children 114 */ 115 public void loadByteArray(SdfBuffer buffer) { 116 List<SdfMacro> l = getChildren(); 117 if (l == null) { 118 return; 119 } 120 for (int i = 0; i < l.size(); i++) { 121 l.get(i).loadByteArray(buffer); 122 } 123 } 124 125 /** 126 * Get the next instruction macro in a buffer. 127 * <p> 128 * Note this uses the index contained in the SdfBuffer implementation, and 129 * has the side-effect of bumping that forward. 130 * 131 * @param buff The SdfBuffer being scanned for instruction macros. 132 * @return Object of SdfMacro subtype for specific next instruction 133 */ 134 static public SdfMacro decodeInstruction(SdfBuffer buff) { 135 SdfMacro m; 136 137 // full 1st byte decoder 138 if ((m = ChannelStart.match(buff)) != null) { 139 return m; 140 } else if ((m = SdlVersion.match(buff)) != null) { 141 return m; 142 } else if ((m = SkemeStart.match(buff)) != null) { 143 return m; 144 } else if ((m = GenerateTrigger.match(buff)) != null) { 145 return m; 146 } else if ((m = EndSound.match(buff)) != null) { 147 return m; 148 } else // 7 bit decode 149 if ((m = DelaySound.match(buff)) != null) { 150 return m; 151 } else // 6 bit decode 152 if ((m = SkipOnTrigger.match(buff)) != null) { 153 return m; 154 } else // 5 bit decode 155 if ((m = InitiateSound.match(buff)) != null) { 156 return m; 157 } else if ((m = MaskCompare.match(buff)) != null) { 158 return m; 159 } else // 4 bit decode 160 if ((m = LoadModifier.match(buff)) != null) { 161 return m; 162 } else if ((m = BranchTo.match(buff)) != null) { 163 return m; 164 } else // 2 bit decode 165 if ((m = Play.match(buff)) != null) { 166 return m; 167 } else // generics 168 if ((m = FourByteMacro.match(buff)) != null) { 169 return m; 170 } else { // only case left is TwoByteMacro, which has to match 171 return TwoByteMacro.match(buff); 172 } 173 } 174 175 /** 176 * Service method to unpack various bit-coded values for display, using a 177 * mask array. 178 * <p> 179 * Note that multiple values can be returned, e.g. this can be used to scan 180 * for individual bits set in a variable. 181 * 182 * @param input Single value to be matched 183 * @param values Array of possible values which the input might match 184 * @param masks Array of masks to be applied when comparing against the 185 * corresponding items in the values array. This is separate 186 * for each possible value to e.g. allow the encoding of a set 187 * of independent bits. 188 * @param labels Should there be a match-under-mask of a value, the 189 * corresponding label is returned 190 * @return "+" separated list of labels, or "<ERROR>" if none matched 191 */ 192 String decodeFlags(int input, int[] values, int[] masks, String[] labels) { 193 String[] names = jmri.util.StringUtil.getNamesFromStateMasked(input, values, masks, labels); 194 if (names.length == 0) { 195 return labels[labels.length - 1]; // last name is non-of-above special case 196 } else if (names.length == 1) { 197 return names[0]; 198 } 199 StringBuilder output = new StringBuilder(names[0]); 200 for (int i = 1; i < names.length; i++) { 201 output.append("+").append(names[i]); 202 } 203 return output.toString(); 204 } 205 206 String decodeState(int input, int[] values, String[] labels) { 207 String val = jmri.util.StringUtil.getNameFromState(input, values, labels); 208 if (val == null) { 209 return labels[labels.length - 1]; 210 } 211 return val; 212 } 213 214 // private final static Logger log = LoggerFactory.getLogger(SdfMacro.class); 215 216}