You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Checksum.java 26 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant.taskdefs;
  19. import java.security.DigestInputStream;
  20. import java.security.MessageDigest;
  21. import java.security.NoSuchAlgorithmException;
  22. import java.security.NoSuchProviderException;
  23. import java.io.File;
  24. import java.io.FileOutputStream;
  25. import java.io.FileInputStream;
  26. import java.io.FileReader;
  27. import java.io.BufferedReader;
  28. import java.io.IOException;
  29. import java.util.Comparator;
  30. import java.util.HashMap;
  31. import java.util.Map;
  32. import java.util.Iterator;
  33. import java.util.Hashtable;
  34. import java.util.Enumeration;
  35. import java.util.Set;
  36. import java.util.Arrays;
  37. import java.text.MessageFormat;
  38. import java.text.ParseException;
  39. import org.apache.tools.ant.BuildException;
  40. import org.apache.tools.ant.Project;
  41. import org.apache.tools.ant.taskdefs.condition.Condition;
  42. import org.apache.tools.ant.types.EnumeratedAttribute;
  43. import org.apache.tools.ant.types.FileSet;
  44. import org.apache.tools.ant.types.Resource;
  45. import org.apache.tools.ant.types.ResourceCollection;
  46. import org.apache.tools.ant.types.resources.FileProvider;
  47. import org.apache.tools.ant.types.resources.Union;
  48. import org.apache.tools.ant.types.resources.Restrict;
  49. import org.apache.tools.ant.types.resources.selectors.Type;
  50. import org.apache.tools.ant.util.FileUtils;
  51. import org.apache.tools.ant.util.StringUtils;
  52. /**
  53. * Used to create or verify file checksums.
  54. *
  55. * @since Ant 1.5
  56. *
  57. * @ant.task category="control"
  58. */
  59. public class Checksum extends MatchingTask implements Condition {
  60. private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  61. private static final int NIBBLE = 4;
  62. private static final int WORD = 16;
  63. private static final int BUFFER_SIZE = 8 * 1024;
  64. private static final int BYTE_MASK = 0xFF;
  65. private static class FileUnion extends Restrict {
  66. private Union u;
  67. FileUnion() {
  68. u = new Union();
  69. super.add(u);
  70. super.add(Type.FILE);
  71. }
  72. public void add(ResourceCollection rc) {
  73. u.add(rc);
  74. }
  75. }
  76. /**
  77. * File for which checksum is to be calculated.
  78. */
  79. private File file = null;
  80. /**
  81. * Root directory in which the checksum files will be written.
  82. * If not specified, the checksum files will be written
  83. * in the same directory as each file.
  84. */
  85. private File todir;
  86. /**
  87. * MessageDigest algorithm to be used.
  88. */
  89. private String algorithm = "MD5";
  90. /**
  91. * MessageDigest Algorithm provider
  92. */
  93. private String provider = null;
  94. /**
  95. * File Extension that is be to used to create or identify
  96. * destination file
  97. */
  98. private String fileext;
  99. /**
  100. * Holds generated checksum and gets set as a Project Property.
  101. */
  102. private String property;
  103. /**
  104. * Holds checksums for all files (both calculated and cached on disk).
  105. * Key: java.util.File (source file)
  106. * Value: java.lang.String (digest)
  107. */
  108. private Map allDigests = new HashMap();
  109. /**
  110. * Holds relative file names for all files (always with a forward slash).
  111. * This is used to calculate the total hash.
  112. * Key: java.util.File (source file)
  113. * Value: java.lang.String (relative file name)
  114. */
  115. private Map relativeFilePaths = new HashMap();
  116. /**
  117. * Property where totalChecksum gets set.
  118. */
  119. private String totalproperty;
  120. /**
  121. * Whether or not to create a new file.
  122. * Defaults to <code>false</code>.
  123. */
  124. private boolean forceOverwrite;
  125. /**
  126. * Contains the result of a checksum verification. ("true" or "false")
  127. */
  128. private String verifyProperty;
  129. /**
  130. * Resource Collection.
  131. */
  132. private FileUnion resources = null;
  133. /**
  134. * Stores SourceFile, DestFile pairs and SourceFile, Property String pairs.
  135. */
  136. private Hashtable includeFileMap = new Hashtable();
  137. /**
  138. * Message Digest instance
  139. */
  140. private MessageDigest messageDigest;
  141. /**
  142. * is this task being used as a nested condition element?
  143. */
  144. private boolean isCondition;
  145. /**
  146. * Size of the read buffer to use.
  147. */
  148. private int readBufferSize = BUFFER_SIZE;
  149. /**
  150. * Formater for the checksum file.
  151. */
  152. private MessageFormat format = FormatElement.getDefault().getFormat();
  153. /**
  154. * Sets the file for which the checksum is to be calculated.
  155. * @param file a <code>File</code> value
  156. */
  157. public void setFile(File file) {
  158. this.file = file;
  159. }
  160. /**
  161. * Sets the root directory where checksum files will be
  162. * written/read
  163. * @param todir the directory to write to
  164. * @since Ant 1.6
  165. */
  166. public void setTodir(File todir) {
  167. this.todir = todir;
  168. }
  169. /**
  170. * Specifies the algorithm to be used to compute the checksum.
  171. * Defaults to "MD5". Other popular algorithms like "SHA" may be used as well.
  172. * @param algorithm a <code>String</code> value
  173. */
  174. public void setAlgorithm(String algorithm) {
  175. this.algorithm = algorithm;
  176. }
  177. /**
  178. * Sets the MessageDigest algorithm provider to be used
  179. * to calculate the checksum.
  180. * @param provider a <code>String</code> value
  181. */
  182. public void setProvider(String provider) {
  183. this.provider = provider;
  184. }
  185. /**
  186. * Sets the file extension that is be to used to
  187. * create or identify destination file.
  188. * @param fileext a <code>String</code> value
  189. */
  190. public void setFileext(String fileext) {
  191. this.fileext = fileext;
  192. }
  193. /**
  194. * Sets the property to hold the generated checksum.
  195. * @param property a <code>String</code> value
  196. */
  197. public void setProperty(String property) {
  198. this.property = property;
  199. }
  200. /**
  201. * Sets the property to hold the generated total checksum
  202. * for all files.
  203. * @param totalproperty a <code>String</code> value
  204. *
  205. * @since Ant 1.6
  206. */
  207. public void setTotalproperty(String totalproperty) {
  208. this.totalproperty = totalproperty;
  209. }
  210. /**
  211. * Sets the verify property. This project property holds
  212. * the result of a checksum verification - "true" or "false"
  213. * @param verifyProperty a <code>String</code> value
  214. */
  215. public void setVerifyproperty(String verifyProperty) {
  216. this.verifyProperty = verifyProperty;
  217. }
  218. /**
  219. * Whether or not to overwrite existing file irrespective of
  220. * whether it is newer than
  221. * the source file. Defaults to false.
  222. * @param forceOverwrite a <code>boolean</code> value
  223. */
  224. public void setForceOverwrite(boolean forceOverwrite) {
  225. this.forceOverwrite = forceOverwrite;
  226. }
  227. /**
  228. * The size of the read buffer to use.
  229. * @param size an <code>int</code> value
  230. */
  231. public void setReadBufferSize(int size) {
  232. this.readBufferSize = size;
  233. }
  234. /**
  235. * Select the in/output pattern via a well know format name.
  236. * @param e an <code>enumerated</code> value
  237. *
  238. * @since 1.7.0
  239. */
  240. public void setFormat(FormatElement e) {
  241. format = e.getFormat();
  242. }
  243. /**
  244. * Specify the pattern to use as a MessageFormat pattern.
  245. *
  246. * <p>{0} gets replaced by the checksum, {1} by the filename.</p>
  247. * @param p a <code>String</code> value
  248. *
  249. * @since 1.7.0
  250. */
  251. public void setPattern(String p) {
  252. format = new MessageFormat(p);
  253. }
  254. /**
  255. * Files to generate checksums for.
  256. * @param set a fileset of files to generate checksums for.
  257. */
  258. public void addFileset(FileSet set) {
  259. add(set);
  260. }
  261. /**
  262. * Add a resource collection.
  263. * @param rc the ResourceCollection to add.
  264. */
  265. public void add(ResourceCollection rc) {
  266. if (rc == null) {
  267. return;
  268. }
  269. resources = (resources == null) ? new FileUnion() : resources;
  270. resources.add(rc);
  271. }
  272. /**
  273. * Calculate the checksum(s).
  274. * @throws BuildException on error
  275. */
  276. public void execute() throws BuildException {
  277. isCondition = false;
  278. boolean value = validateAndExecute();
  279. if (verifyProperty != null) {
  280. getProject().setNewProperty(
  281. verifyProperty,
  282. (value ? Boolean.TRUE.toString() : Boolean.FALSE.toString()));
  283. }
  284. }
  285. /**
  286. * Calculate the checksum(s)
  287. *
  288. * @return Returns true if the checksum verification test passed,
  289. * false otherwise.
  290. * @throws BuildException on error
  291. */
  292. public boolean eval() throws BuildException {
  293. isCondition = true;
  294. return validateAndExecute();
  295. }
  296. /**
  297. * Validate attributes and get down to business.
  298. */
  299. private boolean validateAndExecute() throws BuildException {
  300. String savedFileExt = fileext;
  301. if (file == null && (resources == null || resources.size() == 0)) {
  302. throw new BuildException(
  303. "Specify at least one source - a file or a resource collection.");
  304. }
  305. if (!(resources == null || resources.isFilesystemOnly())) {
  306. throw new BuildException("Can only calculate checksums for file-based resources.");
  307. }
  308. if (file != null && file.exists() && file.isDirectory()) {
  309. throw new BuildException("Checksum cannot be generated for directories");
  310. }
  311. if (file != null && totalproperty != null) {
  312. throw new BuildException("File and Totalproperty cannot co-exist.");
  313. }
  314. if (property != null && fileext != null) {
  315. throw new BuildException("Property and FileExt cannot co-exist.");
  316. }
  317. if (property != null) {
  318. if (forceOverwrite) {
  319. throw new BuildException(
  320. "ForceOverwrite cannot be used when Property is specified");
  321. }
  322. int ct = 0;
  323. if (resources != null) {
  324. ct += resources.size();
  325. }
  326. if (file != null) {
  327. ct++;
  328. }
  329. if (ct > 1) {
  330. throw new BuildException(
  331. "Multiple files cannot be used when Property is specified");
  332. }
  333. }
  334. if (verifyProperty != null) {
  335. isCondition = true;
  336. }
  337. if (verifyProperty != null && forceOverwrite) {
  338. throw new BuildException("VerifyProperty and ForceOverwrite cannot co-exist.");
  339. }
  340. if (isCondition && forceOverwrite) {
  341. throw new BuildException(
  342. "ForceOverwrite cannot be used when conditions are being used.");
  343. }
  344. messageDigest = null;
  345. if (provider != null) {
  346. try {
  347. messageDigest = MessageDigest.getInstance(algorithm, provider);
  348. } catch (NoSuchAlgorithmException noalgo) {
  349. throw new BuildException(noalgo, getLocation());
  350. } catch (NoSuchProviderException noprovider) {
  351. throw new BuildException(noprovider, getLocation());
  352. }
  353. } else {
  354. try {
  355. messageDigest = MessageDigest.getInstance(algorithm);
  356. } catch (NoSuchAlgorithmException noalgo) {
  357. throw new BuildException(noalgo, getLocation());
  358. }
  359. }
  360. if (messageDigest == null) {
  361. throw new BuildException("Unable to create Message Digest", getLocation());
  362. }
  363. if (fileext == null) {
  364. fileext = "." + algorithm;
  365. } else if (fileext.trim().length() == 0) {
  366. throw new BuildException("File extension when specified must not be an empty string");
  367. }
  368. try {
  369. if (resources != null) {
  370. for (Iterator i = resources.iterator(); i.hasNext();) {
  371. Resource r = (Resource) i.next();
  372. File src = ((FileProvider) r.as(FileProvider.class))
  373. .getFile();
  374. if (totalproperty != null || todir != null) {
  375. // Use '/' to calculate digest based on file name.
  376. // This is required in order to get the same result
  377. // on different platforms.
  378. relativeFilePaths.put(src, r.getName().replace(File.separatorChar, '/'));
  379. }
  380. addToIncludeFileMap(src);
  381. }
  382. }
  383. if (file != null) {
  384. if (totalproperty != null || todir != null) {
  385. relativeFilePaths.put(
  386. file, file.getName().replace(File.separatorChar, '/'));
  387. }
  388. addToIncludeFileMap(file);
  389. }
  390. return generateChecksums();
  391. } finally {
  392. fileext = savedFileExt;
  393. includeFileMap.clear();
  394. }
  395. }
  396. /**
  397. * Add key-value pair to the hashtable upon which
  398. * to later operate upon.
  399. */
  400. private void addToIncludeFileMap(File file) throws BuildException {
  401. if (file.exists()) {
  402. if (property == null) {
  403. File checksumFile = getChecksumFile(file);
  404. if (forceOverwrite || isCondition
  405. || (file.lastModified() > checksumFile.lastModified())) {
  406. includeFileMap.put(file, checksumFile);
  407. } else {
  408. log(file + " omitted as " + checksumFile + " is up to date.",
  409. Project.MSG_VERBOSE);
  410. if (totalproperty != null) {
  411. // Read the checksum from disk.
  412. String checksum = readChecksum(checksumFile);
  413. byte[] digest = decodeHex(checksum.toCharArray());
  414. allDigests.put(file, digest);
  415. }
  416. }
  417. } else {
  418. includeFileMap.put(file, property);
  419. }
  420. } else {
  421. String message = "Could not find file "
  422. + file.getAbsolutePath()
  423. + " to generate checksum for.";
  424. log(message);
  425. throw new BuildException(message, getLocation());
  426. }
  427. }
  428. private File getChecksumFile(File file) {
  429. File directory;
  430. if (todir != null) {
  431. // A separate directory was explicitly declared
  432. String path = getRelativeFilePath(file);
  433. directory = new File(todir, path).getParentFile();
  434. // Create the directory, as it might not exist.
  435. directory.mkdirs();
  436. } else {
  437. // Just use the same directory as the file itself.
  438. // This directory will exist
  439. directory = file.getParentFile();
  440. }
  441. File checksumFile = new File(directory, file.getName() + fileext);
  442. return checksumFile;
  443. }
  444. /**
  445. * Generate checksum(s) using the message digest created earlier.
  446. */
  447. private boolean generateChecksums() throws BuildException {
  448. boolean checksumMatches = true;
  449. FileInputStream fis = null;
  450. FileOutputStream fos = null;
  451. byte[] buf = new byte[readBufferSize];
  452. try {
  453. for (Enumeration e = includeFileMap.keys(); e.hasMoreElements();) {
  454. messageDigest.reset();
  455. File src = (File) e.nextElement();
  456. if (!isCondition) {
  457. log("Calculating " + algorithm + " checksum for " + src, Project.MSG_VERBOSE);
  458. }
  459. fis = new FileInputStream(src);
  460. DigestInputStream dis = new DigestInputStream(fis,
  461. messageDigest);
  462. while (dis.read(buf, 0, readBufferSize) != -1) {
  463. // Empty statement
  464. }
  465. dis.close();
  466. fis.close();
  467. fis = null;
  468. byte[] fileDigest = messageDigest.digest ();
  469. if (totalproperty != null) {
  470. allDigests.put(src, fileDigest);
  471. }
  472. String checksum = createDigestString(fileDigest);
  473. //can either be a property name string or a file
  474. Object destination = includeFileMap.get(src);
  475. if (destination instanceof java.lang.String) {
  476. String prop = (String) destination;
  477. if (isCondition) {
  478. checksumMatches
  479. = checksumMatches && checksum.equals(property);
  480. } else {
  481. getProject().setNewProperty(prop, checksum);
  482. }
  483. } else if (destination instanceof java.io.File) {
  484. if (isCondition) {
  485. File existingFile = (File) destination;
  486. if (existingFile.exists()) {
  487. try {
  488. String suppliedChecksum =
  489. readChecksum(existingFile);
  490. checksumMatches = checksumMatches
  491. && checksum.equals(suppliedChecksum);
  492. } catch (BuildException be) {
  493. // file is on wrong format, swallow
  494. checksumMatches = false;
  495. }
  496. } else {
  497. checksumMatches = false;
  498. }
  499. } else {
  500. File dest = (File) destination;
  501. fos = new FileOutputStream(dest);
  502. fos.write(format.format(new Object[] {
  503. checksum,
  504. src.getName(),
  505. FILE_UTILS
  506. .getRelativePath(dest
  507. .getParentFile(),
  508. src),
  509. FILE_UTILS
  510. .getRelativePath(getProject()
  511. .getBaseDir(),
  512. src),
  513. src.getAbsolutePath()
  514. }).getBytes());
  515. fos.write(StringUtils.LINE_SEP.getBytes());
  516. fos.close();
  517. fos = null;
  518. }
  519. }
  520. }
  521. if (totalproperty != null) {
  522. // Calculate the total checksum
  523. // Convert the keys (source files) into a sorted array.
  524. Set keys = allDigests.keySet();
  525. Object[] keyArray = keys.toArray();
  526. // File is Comparable, but sort-order is platform
  527. // dependent (case-insensitive on Windows)
  528. Arrays.sort(keyArray, new Comparator() {
  529. public int compare(Object o1, Object o2) {
  530. File f1 = (File) o1;
  531. File f2 = (File) o2;
  532. return f1 == null ? (f2 == null ? 0 : -1)
  533. : (f2 == null ? 1
  534. : getRelativeFilePath(f1)
  535. .compareTo(getRelativeFilePath(f2)));
  536. }
  537. });
  538. // Loop over the checksums and generate a total hash.
  539. messageDigest.reset();
  540. for (int i = 0; i < keyArray.length; i++) {
  541. File src = (File) keyArray[i];
  542. // Add the digest for the file content
  543. byte[] digest = (byte[]) allDigests.get(src);
  544. messageDigest.update(digest);
  545. // Add the file path
  546. String fileName = getRelativeFilePath(src);
  547. messageDigest.update(fileName.getBytes());
  548. }
  549. String totalChecksum = createDigestString(messageDigest.digest());
  550. getProject().setNewProperty(totalproperty, totalChecksum);
  551. }
  552. } catch (Exception e) {
  553. throw new BuildException(e, getLocation());
  554. } finally {
  555. FileUtils.close(fis);
  556. FileUtils.close(fos);
  557. }
  558. return checksumMatches;
  559. }
  560. private String createDigestString(byte[] fileDigest) {
  561. StringBuffer checksumSb = new StringBuffer();
  562. for (int i = 0; i < fileDigest.length; i++) {
  563. String hexStr = Integer.toHexString(BYTE_MASK & fileDigest[i]);
  564. if (hexStr.length() < 2) {
  565. checksumSb.append("0");
  566. }
  567. checksumSb.append(hexStr);
  568. }
  569. return checksumSb.toString();
  570. }
  571. /**
  572. * Converts an array of characters representing hexadecimal values into an
  573. * array of bytes of those same values. The returned array will be half the
  574. * length of the passed array, as it takes two characters to represent any
  575. * given byte. An exception is thrown if the passed char array has an odd
  576. * number of elements.
  577. *
  578. * NOTE: This code is copied from jakarta-commons codec.
  579. * @param data an array of characters representing hexadecimal values
  580. * @return the converted array of bytes
  581. * @throws BuildException on error
  582. */
  583. public static byte[] decodeHex(char[] data) throws BuildException {
  584. int l = data.length;
  585. if ((l & 0x01) != 0) {
  586. throw new BuildException("odd number of characters.");
  587. }
  588. byte[] out = new byte[l >> 1];
  589. // two characters form the hex value.
  590. for (int i = 0, j = 0; j < l; i++) {
  591. int f = Character.digit(data[j++], WORD) << NIBBLE;
  592. f = f | Character.digit(data[j++], WORD);
  593. out[i] = (byte) (f & BYTE_MASK);
  594. }
  595. return out;
  596. }
  597. /**
  598. * reads the checksum from a file using the specified format.
  599. *
  600. * @since 1.7
  601. */
  602. private String readChecksum(File f) {
  603. BufferedReader diskChecksumReader = null;
  604. try {
  605. diskChecksumReader = new BufferedReader(new FileReader(f));
  606. Object[] result = format.parse(diskChecksumReader.readLine());
  607. if (result == null || result.length == 0 || result[0] == null) {
  608. throw new BuildException("failed to find a checksum");
  609. }
  610. return (String) result[0];
  611. } catch (IOException e) {
  612. throw new BuildException("Couldn't read checksum file " + f, e);
  613. } catch (ParseException e) {
  614. throw new BuildException("Couldn't read checksum file " + f, e);
  615. } finally {
  616. FileUtils.close(diskChecksumReader);
  617. }
  618. }
  619. /**
  620. * @since Ant 1.8.2
  621. */
  622. private String getRelativeFilePath(File f) {
  623. String path = (String) relativeFilePaths.get(f);
  624. if (path == null) {
  625. //bug 37386. this should not occur, but it has, once.
  626. throw new BuildException("Internal error: "
  627. + "relativeFilePaths could not match file "
  628. + f + "\n"
  629. + "please file a bug report on this");
  630. }
  631. return path;
  632. }
  633. /**
  634. * Helper class for the format attribute.
  635. *
  636. * @since 1.7
  637. */
  638. public static class FormatElement extends EnumeratedAttribute {
  639. private static HashMap formatMap = new HashMap();
  640. private static final String CHECKSUM = "CHECKSUM";
  641. private static final String MD5SUM = "MD5SUM";
  642. private static final String SVF = "SVF";
  643. static {
  644. formatMap.put(CHECKSUM, new MessageFormat("{0}"));
  645. formatMap.put(MD5SUM, new MessageFormat("{0} *{1}"));
  646. formatMap.put(SVF, new MessageFormat("MD5 ({1}) = {0}"));
  647. }
  648. /** Constructor for FormatElement */
  649. public FormatElement() {
  650. super();
  651. }
  652. /**
  653. * Get the default value - CHECKSUM.
  654. * @return the defaul value.
  655. */
  656. public static FormatElement getDefault() {
  657. FormatElement e = new FormatElement();
  658. e.setValue(CHECKSUM);
  659. return e;
  660. }
  661. /**
  662. * Convert this enumerated type to a <code>MessageFormat</code>.
  663. * @return a <code>MessageFormat</code> object.
  664. */
  665. public MessageFormat getFormat() {
  666. return (MessageFormat) formatMap.get(getValue());
  667. }
  668. /**
  669. * Get the valid values.
  670. * @return an array of values.
  671. */
  672. public String[] getValues() {
  673. return new String[] {CHECKSUM, MD5SUM, SVF};
  674. }
  675. }
  676. }