1. /*
  2. * Copyright 2002-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs;
  18. import java.io.File;
  19. import java.io.FileInputStream;
  20. import java.io.FileOutputStream;
  21. import java.io.IOException;
  22. import java.util.Properties;
  23. import org.apache.tools.ant.BuildException;
  24. import org.apache.tools.ant.Task;
  25. import org.apache.tools.ant.Project;
  26. import org.apache.tools.ant.util.FileUtils;
  27. /**
  28. * Read, increment, and write a build number in a file
  29. * It will first
  30. * attempt to read a build number from a file, then set the property
  31. * "build.number" to the value that was read in (or 0 if no such value). Then
  32. * it will increment the build number by one and write it back out into the
  33. * file.
  34. *
  35. * @version $Revision: 1.13.2.4 $ $Date: 2004/03/09 17:01:32 $
  36. * @since Ant 1.5
  37. * @ant.task name="buildnumber"
  38. */
  39. public class BuildNumber
  40. extends Task {
  41. /**
  42. * The name of the property in which the build number is stored.
  43. */
  44. private static final String DEFAULT_PROPERTY_NAME = "build.number";
  45. /** The default filename to use if no file specified. */
  46. private static final String DEFAULT_FILENAME = DEFAULT_PROPERTY_NAME;
  47. /** The File in which the build number is stored. */
  48. private File myFile;
  49. /**
  50. * The file in which the build number is stored. Defaults to
  51. * "build.number" if not specified.
  52. *
  53. * @param file the file in which build number is stored.
  54. */
  55. public void setFile(final File file) {
  56. myFile = file;
  57. }
  58. /**
  59. * Run task.
  60. *
  61. * @exception BuildException if an error occurs
  62. */
  63. public void execute()
  64. throws BuildException {
  65. File savedFile = myFile; // may be altered in validate
  66. validate();
  67. final Properties properties = loadProperties();
  68. final int buildNumber = getBuildNumber(properties);
  69. properties.put(DEFAULT_PROPERTY_NAME,
  70. String.valueOf(buildNumber + 1));
  71. // Write the properties file back out
  72. FileOutputStream output = null;
  73. try {
  74. output = new FileOutputStream(myFile);
  75. final String header = "Build Number for ANT. Do not edit!";
  76. properties.store(output, header);
  77. } catch (final IOException ioe) {
  78. final String message = "Error while writing " + myFile;
  79. throw new BuildException(message, ioe);
  80. } finally {
  81. if (null != output) {
  82. try {
  83. output.close();
  84. } catch (final IOException ioe) {
  85. getProject().log("error closing output stream " + ioe, Project.MSG_ERR);
  86. }
  87. }
  88. myFile = savedFile;
  89. }
  90. //Finally set the property
  91. getProject().setNewProperty(DEFAULT_PROPERTY_NAME,
  92. String.valueOf(buildNumber));
  93. }
  94. /**
  95. * Utility method to retrieve build number from properties object.
  96. *
  97. * @param properties the properties to retrieve build number from
  98. * @return the build number or if no number in properties object
  99. * @throws BuildException if build.number property is not an integer
  100. */
  101. private int getBuildNumber(final Properties properties)
  102. throws BuildException {
  103. final String buildNumber =
  104. properties.getProperty(DEFAULT_PROPERTY_NAME, "0").trim();
  105. // Try parsing the line into an integer.
  106. try {
  107. return Integer.parseInt(buildNumber);
  108. } catch (final NumberFormatException nfe) {
  109. final String message =
  110. myFile + " contains a non integer build number: " + buildNumber;
  111. throw new BuildException(message, nfe);
  112. }
  113. }
  114. /**
  115. * Utility method to load properties from file.
  116. *
  117. * @return the loaded properties
  118. * @throws BuildException
  119. */
  120. private Properties loadProperties()
  121. throws BuildException {
  122. FileInputStream input = null;
  123. try {
  124. final Properties properties = new Properties();
  125. input = new FileInputStream(myFile);
  126. properties.load(input);
  127. return properties;
  128. } catch (final IOException ioe) {
  129. throw new BuildException(ioe);
  130. } finally {
  131. if (null != input) {
  132. try {
  133. input.close();
  134. } catch (final IOException ioe) {
  135. getProject().log("error closing input stream " + ioe, Project.MSG_ERR);
  136. }
  137. }
  138. }
  139. }
  140. /**
  141. * Validate that the task parameters are valid.
  142. *
  143. * @throws BuildException if parameters are invalid
  144. */
  145. private void validate()
  146. throws BuildException {
  147. if (null == myFile) {
  148. myFile = getProject().resolveFile(DEFAULT_FILENAME);
  149. }
  150. if (!myFile.exists()) {
  151. try {
  152. FileUtils.newFileUtils().createNewFile(myFile);
  153. } catch (final IOException ioe) {
  154. final String message =
  155. myFile + " doesn't exist and new file can't be created.";
  156. throw new BuildException(message, ioe);
  157. }
  158. }
  159. if (!myFile.canRead()) {
  160. final String message = "Unable to read from " + myFile + ".";
  161. throw new BuildException(message);
  162. }
  163. if (!myFile.canWrite()) {
  164. final String message = "Unable to write to " + myFile + ".";
  165. throw new BuildException(message);
  166. }
  167. }
  168. }