Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

Module.java 25KB

před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
před 21 roky
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. /* *******************************************************************
  2. * Copyright (c) 1999-2001 Xerox Corporation,
  3. * 2002 Palo Alto Research Center, Incorporated (PARC).
  4. * All rights reserved.
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Public License v 2.0
  7. * which accompanies this distribution and is available at
  8. * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
  9. *
  10. * Contributors:
  11. * Xerox/PARC initial implementation
  12. * ******************************************************************/
  13. package org.aspectj.internal.tools.build;
  14. import java.io.BufferedReader;
  15. import java.io.ByteArrayOutputStream;
  16. import java.io.File;
  17. import java.io.FileInputStream;
  18. import java.io.FileReader;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.PrintStream;
  22. import java.util.ArrayList;
  23. import java.util.Iterator;
  24. import java.util.List;
  25. import java.util.ListIterator;
  26. import java.util.Properties;
  27. import java.util.StringTokenizer;
  28. import org.aspectj.internal.tools.build.Result.Kind;
  29. import org.aspectj.internal.tools.build.Util.OSGIBundle;
  30. import org.aspectj.internal.tools.build.Util.OSGIBundle.RequiredBundle;
  31. /**
  32. * This represents an (eclipse) build module/unit used by a Builder to compile
  33. * classes and/or assemble zip file of classes, optionally with all antecedants.
  34. * This implementation infers attributes from two files in the module directory:
  35. * <ul>
  36. * <li>an Eclipse project <code>.classpath</code> file containing required
  37. * libraries and modules (collectively, "antecedants") </li>
  38. * <li>a file <code>{moduleName}.mf.txt</code> is taken as the manifest of
  39. * any .jar file produced, after filtering. </li>
  40. * </ul>
  41. *
  42. * @see Builder
  43. * @see Modules#getModule(String)
  44. */
  45. public class Module {
  46. private static final String[] ATTS = new String[] { "exported", "kind",
  47. "path", "sourcepath" };
  48. // private static final int getATTSIndex(String key) {
  49. // for (int i = 0; i < ATTS.length; i++) {
  50. // if (ATTS[i].equals(key))
  51. // return i;
  52. // }
  53. // return -1;
  54. // }
  55. /**
  56. * @return true if file is null or cannot be read or was last modified after
  57. * time
  58. */
  59. private static boolean outOfDate(long time, File file) {
  60. return ((null == file) || !file.canRead() || (file.lastModified() > time));
  61. }
  62. /** @return all source files under srcDir */
  63. private static Iterator<File> sourceFiles(File srcDir) {
  64. List<File> result = new ArrayList<>();
  65. sourceFiles(srcDir, result);
  66. return result.iterator();
  67. }
  68. private static void sourceFiles(File srcDir, List<File> result) {
  69. if ((null == srcDir) || !srcDir.canRead() || !srcDir.isDirectory()) {
  70. return;
  71. }
  72. File[] files = srcDir.listFiles();
  73. for (File file : files) {
  74. if (file.isDirectory()) {
  75. sourceFiles(file, result);
  76. } else if (isSourceFile(file)) {
  77. result.add(file);
  78. }
  79. }
  80. }
  81. private static void addIfNew(List<File> source, List<File> sink) {
  82. for (File item: source) {
  83. if (!sink.contains(item)) {
  84. sink.add(item);
  85. }
  86. }
  87. }
  88. /**
  89. * Recursively find antecedant jars.
  90. *
  91. * @see findKnownJarAntecedants()
  92. */
  93. static void doFindJarRequirements(Result result, List<File> known) {
  94. Util.iaxIfNull(result, "result");
  95. Util.iaxIfNull(known, "known");
  96. addIfNew(result.getLibJars(), known);
  97. addIfNew(result.getExportedLibJars(), known);
  98. Result[] reqs = result.getRequired();
  99. for (Result requiredResult : reqs) {
  100. File requiredJar = requiredResult.getOutputFile();
  101. if (!known.contains(requiredJar)) {
  102. known.add(requiredJar);
  103. doFindJarRequirements(requiredResult, known);
  104. }
  105. }
  106. }
  107. /** @return true if this is a source file */
  108. private static boolean isSourceFile(File file) {
  109. String path = file.getPath();
  110. return (path.endsWith(".java") || path.endsWith(".aj")); // XXXFileLiteral
  111. }
  112. // /** @return List of File of any module or library jar ending with suffix */
  113. // private static ArrayList findJarsBySuffix(String suffix, Kind kind,
  114. // List libJars, List required) {
  115. // ArrayList result = new ArrayList();
  116. // if (null != suffix) {
  117. // // library jars
  118. // for (Iterator iter = libJars.iterator(); iter.hasNext();) {
  119. // File file = (File) iter.next();
  120. // if (file.getPath().endsWith(suffix)) {
  121. // result.add(file);
  122. // }
  123. // }
  124. // // module jars
  125. // for (Iterator iter = required.iterator(); iter.hasNext();) {
  126. // Module module = (Module) iter.next();
  127. // Result moduleResult = module.getResult(kind);
  128. // File file = moduleResult.getOutputFile();
  129. // if (file.getPath().endsWith(suffix)) {
  130. // result.add(file);
  131. // }
  132. // }
  133. // }
  134. // return result;
  135. // }
  136. public final boolean valid;
  137. public final File moduleDir;
  138. public final String name;
  139. /** reference back to collection for creating required modules */
  140. private final Modules modules;
  141. private final Result release;
  142. private final Result test;
  143. private final Result testAll;
  144. private final Result releaseAll;
  145. /** path to output jar - may not exist */
  146. private final File moduleJar;
  147. /** File list of library jars */
  148. private final List<File> libJars;
  149. /** List of classpath variables */
  150. private final List<String> classpathVariables;
  151. /**
  152. * List of library jars exported to clients (duplicates some libJars
  153. * entries)
  154. */
  155. private final List<File> exportedLibJars;
  156. /** File list of source directories */
  157. private final List<File> srcDirs;
  158. /** properties from the modules {name}.properties file */
  159. private final Properties properties;
  160. /** List of required modules */
  161. private final List<Module> requiredModules;
  162. /** logger */
  163. private final Messager messager;
  164. Module(File moduleDir, File jarDir, String name, Modules modules,
  165. Messager messager) {
  166. Util.iaxIfNotCanReadDir(moduleDir, "moduleDir");
  167. Util.iaxIfNotCanReadDir(jarDir, "jarDir");
  168. Util.iaxIfNull(name, "name");
  169. Util.iaxIfNull(modules, "modules");
  170. this.moduleDir = moduleDir;
  171. this.libJars = new ArrayList<>();
  172. this.exportedLibJars = new ArrayList<>();
  173. this.requiredModules = new ArrayList<>();
  174. this.srcDirs = new ArrayList<>();
  175. this.classpathVariables = new ArrayList<>();
  176. this.properties = new Properties();
  177. this.name = name;
  178. this.modules = modules;
  179. this.messager = messager;
  180. this.moduleJar = new File(jarDir, name + ".jar");
  181. this.release = new Result(Result.RELEASE, this, jarDir);
  182. this.releaseAll = new Result(Result.RELEASE_ALL, this, jarDir);
  183. this.test = new Result(Result.TEST, this, jarDir);
  184. this.testAll = new Result(Result.TEST_ALL, this, jarDir);
  185. valid = init();
  186. }
  187. /** @return Modules registry of known modules, including this one */
  188. public Modules getModules() {
  189. return modules;
  190. }
  191. /**
  192. * @param kind
  193. * the Kind of the result to recalculate
  194. * @param recalculate
  195. * if true, then force recalculation
  196. * @return true if the target jar for this module is older than any source
  197. * files in a source directory or any required modules or any
  198. * libraries or if any libraries or required modules are missing
  199. */
  200. public static boolean outOfDate(Result result) {
  201. File outputFile = result.getOutputFile();
  202. if (!(outputFile.exists() && outputFile.canRead())) {
  203. return true;
  204. }
  205. final long time = outputFile.lastModified();
  206. File file;
  207. for (File srcDir : result.getSrcDirs()) {
  208. for (Iterator<File> srcFiles = sourceFiles(srcDir); srcFiles.hasNext(); ) {
  209. file = srcFiles.next();
  210. if (outOfDate(time, file)) {
  211. return true;
  212. }
  213. }
  214. }
  215. // required modules
  216. Result[] reqs = result.getRequired();
  217. for (Result requiredResult : reqs) {
  218. file = requiredResult.getOutputFile();
  219. if (outOfDate(time, file)) {
  220. return true;
  221. }
  222. }
  223. // libraries
  224. for (File value : result.getLibJars()) {
  225. file = value;
  226. if (outOfDate(time, file)) {
  227. return true;
  228. }
  229. }
  230. return false;
  231. }
  232. public String toString() {
  233. return name;
  234. }
  235. public String toLongString() {
  236. return "Module [name=" + name + ", srcDirs=" + srcDirs + ", required="
  237. + requiredModules + ", moduleJar=" + moduleJar + ", libJars="
  238. + libJars + "]";
  239. }
  240. public Result getResult(Kind kind) {
  241. return kind.assemble ? (kind.normal ? releaseAll : testAll)
  242. : (kind.normal ? release : test);
  243. }
  244. List<File> srcDirs(Result result) {
  245. myResult(result);
  246. return srcDirs;
  247. }
  248. List<File> libJars(Result result) {
  249. myResult(result);
  250. return libJars;
  251. }
  252. List<String> classpathVariables(Result result) {
  253. myResult(result);
  254. return classpathVariables;
  255. }
  256. List<File> exportedLibJars(Result result) {
  257. myResult(result);
  258. return exportedLibJars;
  259. }
  260. List<Module> requiredModules(Result result) {
  261. myResult(result);
  262. return requiredModules;
  263. }
  264. private void myResult(Result result) {
  265. if ((null == result) || this != result.getModule()) {
  266. throw new IllegalArgumentException("not my result: " + result + ": " + this);
  267. }
  268. }
  269. private boolean init() {
  270. boolean cp = initClasspath();
  271. boolean mf = initManifest();
  272. if (!cp && !mf) {
  273. return false;
  274. }
  275. return initProperties() && reviewInit() && initResults();
  276. }
  277. /** read OSGI manifest.mf file XXX hacked */
  278. private boolean initManifest() {
  279. File metaInf = new File(moduleDir, "META-INF");
  280. if (!metaInf.canRead() || !metaInf.isDirectory()) {
  281. return false;
  282. }
  283. File file = new File(metaInf, "MANIFEST.MF"); // XXXFileLiteral
  284. if (!file.exists()) {
  285. return false; // ok, not OSGI
  286. }
  287. InputStream fin = null;
  288. OSGIBundle bundle = null;
  289. try {
  290. fin = new FileInputStream(file);
  291. bundle = new OSGIBundle(fin);
  292. } catch (IOException e) {
  293. messager.logException("IOException reading " + file, e);
  294. return false;
  295. } finally {
  296. Util.closeSilently(fin);
  297. }
  298. RequiredBundle[] bundles = bundle.getRequiredBundles();
  299. for (RequiredBundle required : bundles) {
  300. update("src", "/" + required.name, required.text, false);
  301. }
  302. String[] libs = bundle.getClasspath();
  303. for (String lib : libs) {
  304. update("lib", lib, lib, false);
  305. }
  306. return true;
  307. }
  308. /** read eclipse .classpath file XXX line-oriented hack */
  309. private boolean initClasspath() {
  310. // meaning testsrc directory, junit library, etc.
  311. File file = new File(moduleDir, ".classpath"); // XXXFileLiteral
  312. if (!file.exists()) {
  313. return false; // OSGI???
  314. }
  315. FileReader fin = null;
  316. try {
  317. fin = new FileReader(file);
  318. BufferedReader reader = new BufferedReader(fin);
  319. String line;
  320. XMLItem item = new XMLItem("classpathentry", new ICB());
  321. while (null != (line = reader.readLine())) {
  322. line = line.trim();
  323. // dumb - only handle comment-only lines
  324. if (!line.startsWith("<?xml") && !line.startsWith("<!--")) {
  325. item.acceptLine(line);
  326. }
  327. }
  328. return (0 < (srcDirs.size() + libJars.size()));
  329. } catch (IOException e) {
  330. messager.logException("IOException reading " + file, e);
  331. } finally {
  332. if (null != fin) {
  333. try {
  334. fin.close();
  335. } catch (IOException e) {
  336. } // ignore
  337. }
  338. }
  339. return false;
  340. }
  341. // private boolean update(String toString, String[] attributes) {
  342. // String kind = attributes[getATTSIndex("kind")];
  343. // String path = attributes[getATTSIndex("path")];
  344. // String exp = attributes[getATTSIndex("exported")];
  345. // boolean exported = ("true".equals(exp));
  346. // return update(kind, path, toString, exported);
  347. // }
  348. private boolean update(String kind, String path, String toString,
  349. boolean exported) {
  350. String libPath = null;
  351. if ("src".equals(kind)) {
  352. if (path.startsWith("/")) { // module
  353. String moduleName = path.substring(1);
  354. Module req = modules.getModule(moduleName);
  355. if (null != req) {
  356. requiredModules.add(req);
  357. return true;
  358. } else {
  359. messager.error("update unable to create required module: "
  360. + moduleName);
  361. }
  362. } else { // src dir
  363. String fullPath = getFullPath(path);
  364. File srcDir = new File(fullPath);
  365. if (srcDir.canRead() && srcDir.isDirectory()) {
  366. srcDirs.add(srcDir);
  367. return true;
  368. } else {
  369. messager.error("not a src dir: " + srcDir);
  370. }
  371. }
  372. } else if ("lib".equals(kind)) {
  373. libPath = path;
  374. } else if ("var".equals(kind)) {
  375. final String JAVA_HOME = "JAVA_HOME/";
  376. if (path.startsWith(JAVA_HOME)) {
  377. path = path.substring(JAVA_HOME.length());
  378. String home = System.getProperty("java.home");
  379. if (null != home) {
  380. libPath = Util.path(home, path);
  381. File f = new File(libPath);
  382. if (!f.exists() && home.endsWith("jre")) {
  383. f = new File(home).getParentFile();
  384. libPath = Util.path(f.getPath(), path);
  385. }
  386. }
  387. }
  388. if (null == libPath) {
  389. warnVariable(path, toString);
  390. classpathVariables.add(path);
  391. }
  392. } else if ("con".equals(kind)) {
  393. // 'special' for container pointing at AspectJ runtime...
  394. if (path.equals("org.eclipse.ajdt.core.ASPECTJRT_CONTAINER")) {
  395. classpathVariables.add("ASPECTJRT_LIB");
  396. } else {
  397. if (!path.contains("JRE")) { // warn non-JRE containers
  398. messager.log("cannot handle con yet: " + toString);
  399. }
  400. }
  401. } else if ("out".equals(kind) || "output".equals(kind)) {
  402. // ignore output entries
  403. } else {
  404. messager.log("unrecognized kind " + kind + " in " + toString);
  405. }
  406. if (null != libPath) {
  407. File libJar = new File(libPath);
  408. if (!libJar.exists()) {
  409. libJar = new File(getFullPath(libPath));
  410. }
  411. if (libJar.canRead() && libJar.isFile()) {
  412. libJars.add(libJar);
  413. if (exported) {
  414. exportedLibJars.add(libJar);
  415. }
  416. return true;
  417. } else {
  418. messager.error("no such library jar " + libJar + " from "
  419. + toString);
  420. }
  421. }
  422. return false;
  423. }
  424. private void warnVariable(String path, String toString) {
  425. String[] known = { "JRE_LIB", "ASPECTJRT_LIB", "JRE15_LIB" };
  426. for (String s : known) {
  427. if (s.equals(path)) {
  428. return;
  429. }
  430. }
  431. messager.log("Module cannot handle var yet: " + toString);
  432. }
  433. /** @return true if any properties were read correctly */
  434. private boolean initProperties() {
  435. File file = new File(moduleDir, name + ".properties"); // XXXFileLiteral
  436. if (!Util.canReadFile(file)) {
  437. return true; // no properties to read
  438. }
  439. FileInputStream fin = null;
  440. try {
  441. fin = new FileInputStream(file);
  442. properties.load(fin);
  443. return true;
  444. } catch (IOException e) {
  445. messager.logException("IOException reading " + file, e);
  446. return false;
  447. } finally {
  448. if (null != fin) {
  449. try {
  450. fin.close();
  451. } catch (IOException e) {
  452. } // ignore
  453. }
  454. }
  455. }
  456. /**
  457. * Post-process initialization. This implementation trims java5 source dirs
  458. * if not running in a Java 5 VM.
  459. * @return true if initialization post-processing worked
  460. */
  461. protected boolean reviewInit() {
  462. try {
  463. for (ListIterator<File> iter = srcDirs.listIterator(); iter.hasNext();) {
  464. File srcDir = iter.next();
  465. String lcname = srcDir.getName().toLowerCase();
  466. if (!Util.JAVA5_VM
  467. && (Util.Constants.JAVA5_SRC.equals(lcname) || Util.Constants.JAVA5_TESTSRC
  468. .equals(lcname))) {
  469. // assume optional for pre-1.5 builds
  470. iter.remove();
  471. }
  472. }
  473. } catch (UnsupportedOperationException e) {
  474. return false; // failed XXX log also if verbose
  475. }
  476. return true;
  477. }
  478. /**
  479. * After reviewInit, setup four kinds of results.
  480. */
  481. protected boolean initResults() {
  482. return true; // results initialized lazily
  483. }
  484. /** resolve path absolutely, assuming / means base of modules dir */
  485. public String getFullPath(String path) {
  486. String fullPath;
  487. if (path.startsWith("/")) {
  488. fullPath = modules.baseDir.getAbsolutePath() + path;
  489. } else {
  490. fullPath = moduleDir.getAbsolutePath() + "/" + path;
  491. }
  492. // check for absolute paths (untested - none in our modules so far)
  493. File testFile = new File(fullPath);
  494. // System.out.println("Module.getFullPath: " + fullPath + " - " +
  495. // testFile.getAbsolutePath());
  496. if (!testFile.exists()) {
  497. testFile = new File(path);
  498. if (testFile.exists() && testFile.isAbsolute()) {
  499. fullPath = path;
  500. }
  501. }
  502. return fullPath;
  503. }
  504. class ICB implements XMLItem.ICallback {
  505. public void end(Properties attributes) {
  506. String kind = attributes.getProperty("kind");
  507. String path = attributes.getProperty("path");
  508. String exp = attributes.getProperty("exported");
  509. boolean exported = ("true".equals(exp));
  510. ByteArrayOutputStream bout = new ByteArrayOutputStream();
  511. attributes.list(new PrintStream(bout));
  512. update(kind, path, bout.toString(), exported);
  513. }
  514. }
  515. public static class XMLItem {
  516. public interface ICallback {
  517. void end(Properties attributes);
  518. }
  519. static final String START_NAME = "classpathentry";
  520. static final String ATT_STARTED = "STARTED";
  521. final ICallback callback;
  522. final StringBuffer input = new StringBuffer();
  523. final String[] attributes = new String[ATTS.length];
  524. final String targetEntity;
  525. String entityName;
  526. String attributeName;
  527. XMLItem(String targetEntity, ICallback callback) {
  528. this.callback = callback;
  529. this.targetEntity = targetEntity;
  530. reset();
  531. }
  532. private void reset() {
  533. input.setLength(0);
  534. for (int i = 0; i < attributes.length; i++) {
  535. attributes[i] = null;
  536. }
  537. entityName = null;
  538. attributeName = null;
  539. }
  540. String[] tokenize(String line) {
  541. final String DELIM = " \n\t\\<>\"=";
  542. StringTokenizer st = new StringTokenizer(line, DELIM, true);
  543. ArrayList<String> result = new ArrayList<>();
  544. StringBuilder quote = new StringBuilder();
  545. boolean inQuote = false;
  546. while (st.hasMoreTokens()) {
  547. String s = st.nextToken();
  548. if ((1 == s.length()) && (DELIM.contains(s))) {
  549. if ("\"".equals(s)) { // end quote (or escaped)
  550. if (inQuote) {
  551. inQuote = false;
  552. quote.append("\"");
  553. result.add(quote.toString());
  554. quote.setLength(0);
  555. } else {
  556. quote.append("\"");
  557. inQuote = true;
  558. }
  559. } else {
  560. result.add(s);
  561. }
  562. } else { // not a delimiter
  563. if (inQuote) {
  564. quote.append(s);
  565. } else {
  566. result.add(s);
  567. }
  568. }
  569. }
  570. return result.toArray(new String[0]);
  571. }
  572. public void acceptLine(String line) {
  573. String[] tokens = tokenize(line);
  574. for (String token : tokens) {
  575. next(token);
  576. }
  577. }
  578. private Properties attributesToProperties() {
  579. Properties result = new Properties();
  580. for (int i = 0; i < attributes.length; i++) {
  581. String a = attributes[i];
  582. if (null != a) {
  583. result.setProperty(ATTS[i], a);
  584. }
  585. }
  586. return result;
  587. }
  588. void errorIfNotNull(String name, String value) {
  589. if (null != value) {
  590. error("Did not expect " + name + ": " + value);
  591. }
  592. }
  593. void errorIfNull(String name, String value) {
  594. if (null == value) {
  595. error("expected value for " + name);
  596. }
  597. }
  598. boolean activeEntity() {
  599. return targetEntity.equals(entityName);
  600. }
  601. /**
  602. * Assumes that comments and "<?xml"-style lines are removed.
  603. */
  604. public void next(String s) {
  605. if ((null == s) || (0 == s.length())) {
  606. return;
  607. }
  608. input.append(s);
  609. s = s.trim();
  610. if (0 == s.length()) {
  611. return;
  612. }
  613. if ("<".equals(s)) {
  614. errorIfNotNull("entityName", entityName);
  615. errorIfNotNull("attributeName", attributeName);
  616. } else if (">".equals(s)) {
  617. errorIfNull("entityName", entityName);
  618. if ("/".equals(attributeName)) {
  619. attributeName = null;
  620. } else {
  621. errorIfNotNull("attributeName", attributeName);
  622. }
  623. if (activeEntity()) {
  624. callback.end(attributesToProperties());
  625. }
  626. entityName = null;
  627. } else if ("=".equals(s)) {
  628. errorIfNull("entityName", entityName);
  629. errorIfNull("attributeName", attributeName);
  630. } else if (s.startsWith("\"")) {
  631. errorIfNull("entityName", entityName);
  632. errorIfNull("attributeName", attributeName);
  633. writeAttribute(attributeName, s);
  634. attributeName = null;
  635. } else {
  636. if (null == entityName) {
  637. reset();
  638. entityName = s;
  639. } else if (null == attributeName) {
  640. attributeName = s;
  641. } else {
  642. System.out
  643. .println("unknown state - not value, attribute, or entity: "
  644. + s);
  645. }
  646. }
  647. }
  648. void readAttribute(String s) {
  649. for (int i = 0; i < ATTS.length; i++) {
  650. if (s.equals(ATTS[i])) {
  651. attributes[i] = ATT_STARTED;
  652. break;
  653. }
  654. }
  655. }
  656. void writeAttribute(String name, String value) {
  657. for (int i = 0; i < ATTS.length; i++) {
  658. if (name.equals(ATTS[i])) {
  659. if (!value.startsWith("\"") || !value.endsWith("\"")) {
  660. error("bad attribute value: " + value);
  661. }
  662. value = value.substring(1, value.length() - 1);
  663. attributes[i] = value;
  664. return;
  665. }
  666. }
  667. }
  668. void error(String s) {
  669. throw new Error(s + " at input " + input);
  670. }
  671. }
  672. }