1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
|
/*
* GenerateSource.
* Generates source code for multiple API's from a single source tree using
* #ifdef, #else, #endif tags.
*
* Copyright (C) 1999 Paul Siegmann <pauls@euronet.nl>
*
* 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
*/
import java.io.*;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* GenerateSource: generate java source code for multiple API's from
* a single source tree using #ifdef, #else, #endif tags.
* <BR>
* This class generates the source code for a particular API version from
* java source code which has been "enriched" with several tags.<BR>
* <B>How to use the program:</B><BR>
* From usage:<BR>
* usage: GenerateSource -d output_directory -t tagname1 -t tagnameN [source files]<BR>
* <DL>
* <DT>-d output_directory
* <DD>A required parameter, the directory where the resulting files should be written to.<BR>
* <B>Note:</B> The current version will happily accept "." as output directory
* or the same sourcefile as targetfile, thus overwriting all your files.<BR>
* High on the TODO list.<DD>
* <DT>-t tagname
* <DD>Define a tag this way. Multiple tags are defined using multiple -t switches.<BR>
* Valid tags start with A-Za-z and after that any number of A-Za-z0-9_ chars.<BR>
* </DL>
* <P>
* <B>Using tags in the sourcecode:</B><BR>
* Currently understood tags are:
* <DL>
* <DT>#ifdef <tagname> <more tagnames>
* <DD>every line after the #ifdef line is only written if <EM>any</EM> of
* the tagnames are defined.<BR>
* Valid tags start with A-Za-z and after that any number of A-Za-z0-9_ chars.<BR>
* Parsing the tagline will stop at the first invalid tag.<BR>
* So a line like: "#ifdef VERSION_2_1 // only do this in VERSION_2_1" will
* be read as "#ifdef VERSION_2_1".<BR>
* Note that '=' as the begin of a tagName is reserved for future use.<BR>
* </DD>
* <DT>#else
* <DD>with this tag the writeStatus is negated.<BR>
* If the program was currently writing lines based on the previous #ifdef
* condition, it will stop writing.<BR>
* If on the other hand the program was currently <EM>not</EM> writing lines
* based on the previous #ifdef condition, it will start doing so.</DD>
* <DT>#endif
* <DD>The effect of the previous #ifdef line will be undone</DD>
* <P>
* <DT>Example use:</DT>
* <DD>
* <PRE>
* #ifdef TagName_1_1 TagName_1_2 // If either of TagName_1_1 or TagName_1_2
* // is defined writing will start now
* ...
*
* #ifdef TagName_2_1 // from now on (TagName_1_1 or Tagname_1_2)
* // and TagName_2_1 must be defined
* ...
*
* #else // from now on (TagName_1_1 or Tagname_1_2)
* // and not TagName_2_1 must be defined
* ...
*
* #endif // If either of TagName_1_1 or TagName_1_2 is
* // defined writing will take place
* </PRE>
* <DD>
* </DL>
* <P>
* Remarks:
* <UL>
* <LI>Tags start with a '#' and may be preceded with spaces and tabs.<BR>
* In fact anything lower than \u0020 is ignored.
* <LI>Lines starting with unknown tags are written.<BR>
* Example: The line "#helloworld ..." is written as "#helloworld..."
* <LI>When a known tag is preceded with an extra '#' the line is written
* with the extra '#' removed.<BR>
* Example: A line starting with ##endif... is written as #endif...
* <LI>Tags within comments are used<BR>
* <DL>
* <DT>Example:
* <DD>
* <PRE>
* /**
* #ifdef VERSION_2_1
* * @deprecated
* #else // VERSION_2_0
* * A hot new method
* #endif
* */
* </PRE>
* <DD>
* <DT>if VERSION_2_1 is defined this is written as:
* <DD><PRE>@deprecated</PRE></DD>
* <DT>And if not:
* <DD>
* <PRE>
* A hot new method
* </PRE>
* </DD>
* <DT>The reason for this is that sometimes a new api version puts a @deprecated tag withing a comment.<BR>
* This is also one of the reasons this class was written instead of just using cpp.
* </DL>
* </UL>
*/
public class GenerateSource {
final public static String VALID_TAG_CHAR_BEGIN = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
final public static String VALID_TAG_CHAR = VALID_TAG_CHAR_BEGIN + "01234567890_";
private Vector sourceFiles;
private String targetDirName = null;
private boolean myWriteStatus;
private BufferedReader in;
private BufferedWriter out;
private Hashtable tagTable;
private int lineNr;
private String tagSet = "";
private Vector activeTags;
public static void main(String[] args) {
try {
GenerateSource gs = new GenerateSource(args);
gs.execute();
} catch(IllegalArgumentException e) {
GenerateSource.usage();
}
}
public GenerateSource(String[] args) throws IllegalArgumentException {
sourceFiles = new Vector();
tagTable = new Hashtable();
processArgs(args);
}
public static void usage() {
System.out.println("usage: GenerateSource");
System.out.println("\t-d <directory> Destination directory for output files");
System.out.println("\t-t <tagname> A tagname");
}
private void processArgs(String[] args) throws IllegalArgumentException {
try {
for(int i = 0; i < args.length; i++) {
if(args[i].equals("-d")) {
i++;
targetDirName = args[i];
if(targetDirName.charAt(targetDirName.length()-1) != File.separatorChar) {
targetDirName += File.separatorChar;
}
} else if(args[i].equals("-t")) {
i++;
tagTable.put(args[i],"");
tagSet += " " + args[i];
} else {
sourceFiles.addElement(args[i]);
}
}
} catch(ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException();
}
if(targetDirName == null) {
throw new IllegalArgumentException();
}
}
public void execute() {
activeTags = new Vector();
String inFileName;
String outFileName = null;
for(int i = 0; i < sourceFiles.size(); i++) {
myWriteStatus = true;
inFileName = (String)(sourceFiles.elementAt(i));
try {
in = new BufferedReader(new FileReader(inFileName));
outFileName = createOutFileName(inFileName);
out = new BufferedWriter(new FileWriter(outFileName));
writeGeneratedMessage(out,inFileName);
System.out.println(inFileName + " => " + outFileName);
String line = in.readLine();
while(line != null) {
lineNr++;
if(hasTag(line)) {
processTagLine(line);
} else if(writeStatus()) {
writeLine(line);
}
line = in.readLine();
}
out.flush();
out.close();
} catch(FileNotFoundException e) {
System.err.println("File not found: \"" + inFileName + "\"");
} catch(IOException f) {
System.err.println("IOError in file \"" + inFileName + "\" or \"" + outFileName + "\" :" + f);
}
}
}
private void writeGeneratedMessage(BufferedWriter out, String inFileName) throws IOException {
out.write(
"////////////////////////////////////////////////////////////////////////////");
out.newLine();
out.write("//\tGENERATED FILE");
out.newLine();
out.write("//\tINPUT FILE: " + inFileName);
out.newLine();
// tagSet begins with a ' '
out.write("//\tTAGS USED:" + tagSet);
out.newLine();
out.write("//");
out.newLine();
out.write("//\tDO NOT EDIT!");
out.newLine();
out.write(
"////////////////////////////////////////////////////////////////////////////");
out.newLine();
out.newLine();
}
private void writeLine(String line) throws IOException {
out.write(line,0,line.length());
out.newLine();
}
private String createOutFileName(String inFileName) {
String outFileName = inFileName;
try {
outFileName = inFileName.substring(inFileName.lastIndexOf(File.separatorChar)+1);
} catch(StringIndexOutOfBoundsException e) {
}
return targetDirName + outFileName;
}
private void processTagLine(String line) throws IOException {
StringTokenizer st = new StringTokenizer(line);
String tag = st.nextToken();
// System.out.println("tag: " + tag);
if(tag.equals("#endif")) {
try {
activeTags.removeElementAt(activeTags.size() - 1);
} catch(ArrayIndexOutOfBoundsException e) {
}
myWriteStatus = checkWriteStatus();
} else if(tag.equals("#ifdef")) {
if(! st.hasMoreTokens()) {
System.err.println("illegal tag in line " + lineNr);
}
Vector tagNames = new Vector();
String tagName;
while(st.hasMoreTokens()) {
tagName = st.nextToken();
if(VALID_TAG_CHAR_BEGIN.indexOf(tagName.charAt(0)) != -1)
{
tagNames.addElement(tagName);
// System.out.println("tagName: " + tagName);
} else {
break;
}
}
String[] result = new String[tagNames.size()];
tagNames.copyInto(result);
activeTags.addElement(result);
myWriteStatus = checkWriteStatus();
} else if(tag.equals("#else")) {
myWriteStatus = checkWriteStatus(true);
} else if((tag.equals("##endif"))
||(tag.equals("##ifdef"))
||(tag.equals("##else"))) {
writeLine(line.substring(0,line.indexOf('#')) + line.substring(line.indexOf('#') + 1));
} else {
writeLine(line);
}
}
/**
* checks whether every String[] in activeTags contains at least
* a defined tag.
*/
private boolean checkWriteStatus() {
return checkWriteStatus(false);
}
private boolean checkWriteStatus(boolean elseTag) {
String[] currentTagSet;
boolean foundIt;
int checkSize;
if(elseTag) {
checkSize = activeTags.size()-1;
} else {
checkSize = activeTags.size();
}
for(int i = 0; i < checkSize; i++) {
foundIt = false;
currentTagSet = (String[])activeTags.elementAt(i);
for(int j = 0; j < currentTagSet.length; j++) {
if(tagTable.containsKey(currentTagSet[j])) {
foundIt = true;
break;
}
}
if(foundIt == false) {
return false;
}
}
if(elseTag) {
currentTagSet = (String[])activeTags.lastElement();
for(int j = 0; j < currentTagSet.length; j++) {
if(tagTable.containsKey(currentTagSet[j])) {
return false;
}
}
}
return true;
}
private boolean writeStatus() {
return myWriteStatus;
}
private boolean hasTag(String line) {
for(int i = 0; i < line.length(); i++) {
if(line.charAt(i) == '#') {
return true;
} else if(line.charAt(i) > '\u0020') {
break;
}
}
return false;
}
}
|