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

Numeric.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. /*
  2. * $Id$
  3. * ============================================================================
  4. * The Apache Software License, Version 1.1
  5. * ============================================================================
  6. *
  7. * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without modifica-
  10. * tion, are permitted provided that the following conditions are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright notice,
  13. * this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright notice,
  16. * this list of conditions and the following disclaimer in the documentation
  17. * and/or other materials provided with the distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if any, must
  20. * include the following acknowledgment: "This product includes software
  21. * developed by the Apache Software Foundation (http://www.apache.org/)."
  22. * Alternately, this acknowledgment may appear in the software itself, if
  23. * and wherever such third-party acknowledgments normally appear.
  24. *
  25. * 4. The names "FOP" and "Apache Software Foundation" must not be used to
  26. * endorse or promote products derived from this software without prior
  27. * written permission. For written permission, please contact
  28. * apache@apache.org.
  29. *
  30. * 5. Products derived from this software may not be called "Apache", nor may
  31. * "Apache" appear in their name, without prior written permission of the
  32. * Apache Software Foundation.
  33. *
  34. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
  35. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  36. * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  37. * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  38. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
  39. * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  40. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  41. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  42. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  43. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. * ============================================================================
  45. *
  46. * This software consists of voluntary contributions made by many individuals
  47. * on behalf of the Apache Software Foundation and was originally created by
  48. * James Tauber <jtauber@jtauber.com>. For more information on the Apache
  49. * Software Foundation, please see <http://www.apache.org/>.
  50. */
  51. package org.apache.fop.fo.expr;
  52. import java.util.Vector;
  53. import org.apache.fop.datatypes.Length;
  54. import org.apache.fop.datatypes.FixedLength;
  55. import org.apache.fop.datatypes.PercentLength;
  56. import org.apache.fop.datatypes.MixedLength;
  57. import org.apache.fop.datatypes.TableColLength;
  58. import org.apache.fop.datatypes.PercentBase;
  59. /**
  60. * Represents a "numeric" value as defined by the XSL FO Specification.
  61. * This consists of one or more kinds of value specifications, from
  62. * absolute numbers (units power of 0) to lengths (unit power of 1),
  63. * relative lengths (ems), percentage lengths.
  64. * A Numeric can be constructed from other Property types representing
  65. * Numbers or Length-type values.
  66. * Numeric provides methods to return Number and Length values based on
  67. * its current value.
  68. * It supports basic arithmetic operations involving Numerics.
  69. */
  70. public class Numeric {
  71. // Bit fields
  72. public static final int ABS_LENGTH = 1; // abs units (or number)
  73. public static final int PC_LENGTH = 2; // Percentage
  74. public static final int TCOL_LENGTH = 4; // Table units
  75. private int valType;
  76. private double absValue;
  77. private double pcValue;
  78. private PercentBase pcBase = null; // base value for PC_LENGTH component
  79. private double tcolValue;
  80. private int dim;
  81. /**
  82. * Construct a Numeric object by specifying one or more components,
  83. * including absolute length, percent length, table units.
  84. * @param valType A combination of bits representing the value types.
  85. * @param absValue The value of a Number or resolved Length value if
  86. * the ABS_LENGTH flag is set.
  87. * @param pcValue The decimal percent value if the PC_LENGTH flag is set
  88. * @param tcolValue The decimal table unit value if the TCOL_LENGTH flag
  89. * is set.
  90. * @param dim The dimension of the value. 0 for a Number, 1 for a Length
  91. * (any type), >1, <0 if Lengths have been multiplied or divided.
  92. * @pcBase The PercentBase object used to calculate an actual value for
  93. * a PC_LENGTH.
  94. */
  95. protected Numeric(int valType, double absValue, double pcValue,
  96. double tcolValue, int dim, PercentBase pcBase) {
  97. this.valType = valType;
  98. this.absValue = absValue;
  99. this.pcValue = pcValue;
  100. this.tcolValue = tcolValue;
  101. this.dim = dim;
  102. this.pcBase = pcBase;
  103. }
  104. /**
  105. * Construct a Numeric object of dimension 0 from a double.
  106. * @param valType A combination of bits representing the value types.
  107. * @param absValue The value of a Number or resolved Length value.
  108. */
  109. /**
  110. * *
  111. * protected Numeric(int valType, double absValue) {
  112. * this.valType = valType;
  113. * this.absValue = absValue;
  114. * }
  115. */
  116. /**
  117. * Construct a Numeric object from a Number.
  118. * @param num The number.
  119. */
  120. public Numeric(Number num) {
  121. this(ABS_LENGTH, num.doubleValue(), 0.0, 0.0, 0, null);
  122. }
  123. /**
  124. * Construct a Numeric object from a Length.
  125. * @param l The Length.
  126. */
  127. public Numeric(FixedLength l) {
  128. this(ABS_LENGTH, (double)l.getValue(), 0.0, 0.0, 1, null);
  129. }
  130. /**
  131. * Construct a Numeric object from a PercentLength.
  132. * @param pclen The PercentLength.
  133. */
  134. public Numeric(PercentLength pclen) {
  135. this(PC_LENGTH, 0.0, pclen.value(), 0.0, 1, pclen.getBaseLength());
  136. }
  137. /**
  138. * Construct a Numeric object from a TableColLength.
  139. * @param tclen The TableColLength.
  140. */
  141. public Numeric(TableColLength tclen) {
  142. this(TCOL_LENGTH, 0.0, 0.0, tclen.getTableUnits(), 1, null);
  143. }
  144. /**
  145. * Return the current value as a Length if possible. This constructs
  146. * a new Length or Length subclass based on the current value type
  147. * of the Numeric.
  148. * If the stored value has a unit dimension other than 1, null
  149. * is returned.
  150. */
  151. public Length asLength() {
  152. if (dim == 1) {
  153. Vector len = new Vector(3);
  154. if ((valType & ABS_LENGTH) != 0) {
  155. len.add(new FixedLength((int)absValue));
  156. }
  157. if ((valType & PC_LENGTH) != 0) {
  158. len.add(new PercentLength(pcValue, pcBase));
  159. }
  160. if ((valType & TCOL_LENGTH) != 0) {
  161. len.add(new TableColLength(tcolValue));
  162. }
  163. if (len.size() == 1) {
  164. return (Length)len.elementAt(0);
  165. } else {
  166. return new MixedLength(len);
  167. }
  168. } else {
  169. // or throw exception???
  170. // can't make Length if dimension != 1
  171. return null;
  172. }
  173. }
  174. /**
  175. * Return the current value as a Number if possible.
  176. * Calls asDouble().
  177. */
  178. public Number asNumber() {
  179. return asDouble();
  180. }
  181. public Double asDouble() {
  182. if (dim == 0 && valType == ABS_LENGTH) {
  183. return new Double(absValue);
  184. } else {
  185. // or throw exception???
  186. // can't make Number if dimension != 0
  187. return null;
  188. }
  189. }
  190. /**
  191. * Return the current value as a Integer if possible.
  192. * If the unit dimension is 0 and the value type is ABSOLUTE, an Integer
  193. * is returned. Otherwise null is returned. Note: the current value is
  194. * truncated if necessary to make an integer value.
  195. */
  196. /**
  197. * public Integer asInteger() {
  198. * if (dim == 0 && valType==ABS_LENGTH) {
  199. * return new Integer((int)absValue);
  200. * }
  201. * else {
  202. * // or throw exception???
  203. * // can't make Number if dimension != 0
  204. * return null;
  205. * }
  206. * }
  207. */
  208. /**
  209. * Return a boolean value indiciating whether the currently stored
  210. * value consists of different "types" of values (absolute, percent,
  211. * and/or table-unit.)
  212. */
  213. private boolean isMixedType() {
  214. int ntype = 0;
  215. for (int t = valType; t != 0; t = t >> 1) {
  216. if ((t & 1) != 0) {
  217. ++ntype;
  218. }
  219. }
  220. return ntype > 1;
  221. }
  222. /**
  223. * Subtract the operand from the current value and return a new Numeric
  224. * representing the result.
  225. * @param op The value to subtract.
  226. * @return A Numeric representing the result.
  227. * @throws PropertyException If the dimension of the operand is different
  228. * from the dimension of this Numeric.
  229. */
  230. public Numeric subtract(Numeric op) throws PropertyException {
  231. // Check of same dimension
  232. // Add together absolute and table units
  233. // What about percentages??? Treat as colUnits if they can't be
  234. // in same property!
  235. if (dim == op.dim) {
  236. PercentBase npcBase = ((valType & PC_LENGTH) != 0) ? pcBase
  237. : op.pcBase;
  238. // Subtract each type of value
  239. return new Numeric(valType | op.valType, absValue - op.absValue,
  240. pcValue - op.pcValue,
  241. tcolValue - op.tcolValue, dim, npcBase);
  242. } else {
  243. throw new PropertyException("Can't add Numerics of different dimensions");
  244. }
  245. }
  246. /**
  247. * Add the operand from the current value and return a new Numeric
  248. * representing the result.
  249. * @param op The value to add.
  250. * @return A Numeric representing the result.
  251. * @throws PropertyException If the dimension of the operand is different
  252. * from the dimension of this Numeric.
  253. */
  254. public Numeric add(Numeric op) throws PropertyException {
  255. // Check of same dimension
  256. // Add together absolute and table units
  257. // What about percentages??? Treat as colUnits if they can't be
  258. // in same property!
  259. if (dim == op.dim) {
  260. PercentBase npcBase = ((valType & PC_LENGTH) != 0) ? pcBase
  261. : op.pcBase;
  262. // Add each type of value
  263. return new Numeric(valType | op.valType, absValue + op.absValue,
  264. pcValue + op.pcValue,
  265. tcolValue + op.tcolValue, dim, npcBase);
  266. } else {
  267. throw new PropertyException("Can't add Numerics of different dimensions");
  268. }
  269. }
  270. /**
  271. * Multiply the the current value by the operand and return a new Numeric
  272. * representing the result.
  273. * @param op The multiplier.
  274. * @return A Numeric representing the result.
  275. * @throws PropertyException If both Numerics have "mixed" type.
  276. */
  277. public Numeric multiply(Numeric op) throws PropertyException {
  278. // Multiply together absolute units and add dimensions (exponents)
  279. // What about percentages??? Treat as colUnits if they can't be
  280. // in same property!
  281. if (dim == 0) {
  282. // This is a dimensionless quantity, ie. a "Number"
  283. return new Numeric(op.valType, absValue * op.absValue,
  284. absValue * op.pcValue,
  285. absValue * op.tcolValue, op.dim, op.pcBase);
  286. } else if (op.dim == 0) {
  287. double opval = op.absValue;
  288. return new Numeric(valType, opval * absValue, opval * pcValue,
  289. opval * tcolValue, dim, pcBase);
  290. } else if (valType == op.valType && !isMixedType()) {
  291. // Check same relbase and pcbase ???
  292. PercentBase npcBase = ((valType & PC_LENGTH) != 0) ? pcBase
  293. : op.pcBase;
  294. return new Numeric(valType, absValue * op.absValue,
  295. pcValue * op.pcValue,
  296. tcolValue * op.tcolValue, dim + op.dim,
  297. npcBase);
  298. } else {
  299. throw new PropertyException("Can't multiply mixed Numerics");
  300. }
  301. }
  302. /**
  303. * Divide the the current value by the operand and return a new Numeric
  304. * representing the result.
  305. * @param op The divisor.
  306. * @return A Numeric representing the result.
  307. * @throws PropertyException If both Numerics have "mixed" type.
  308. */
  309. public Numeric divide(Numeric op) throws PropertyException {
  310. // Multiply together absolute units and add dimensions (exponents)
  311. // What about percentages??? Treat as colUnits if they can't be
  312. // in same property!
  313. if (dim == 0) {
  314. // This is a dimensionless quantity, ie. a "Number"
  315. return new Numeric(op.valType, absValue / op.absValue,
  316. absValue / op.pcValue,
  317. absValue / op.tcolValue, -op.dim, op.pcBase);
  318. } else if (op.dim == 0) {
  319. double opval = op.absValue;
  320. return new Numeric(valType, absValue / opval, pcValue / opval,
  321. tcolValue / opval, dim, pcBase);
  322. } else if (valType == op.valType && !isMixedType()) {
  323. PercentBase npcBase = ((valType & PC_LENGTH) != 0) ? pcBase
  324. : op.pcBase;
  325. return new Numeric(valType,
  326. (valType == ABS_LENGTH ? absValue / op.absValue : 0.0),
  327. (valType == PC_LENGTH ? pcValue / op.pcValue : 0.0),
  328. (valType == TCOL_LENGTH ? tcolValue / op.tcolValue : 0.0),
  329. dim - op.dim, npcBase);
  330. } else {
  331. throw new PropertyException("Can't divide mixed Numerics.");
  332. }
  333. }
  334. /**
  335. * Return the absolute value of this Numeric.
  336. * @return A new Numeric object representing the absolute value.
  337. */
  338. public Numeric abs() {
  339. return new Numeric(valType, Math.abs(absValue), Math.abs(pcValue),
  340. Math.abs(tcolValue), dim, pcBase);
  341. }
  342. /**
  343. * Return a Numeric which is the maximum of the current value and the
  344. * operand.
  345. * @throws PropertyException If the dimensions or value types of the
  346. * object and the operand are different.
  347. */
  348. public Numeric max(Numeric op) throws PropertyException {
  349. double rslt = 0.0;
  350. // Only compare if have same dimension and value type!
  351. if (dim == op.dim && valType == op.valType && !isMixedType()) {
  352. if (valType == ABS_LENGTH) {
  353. rslt = absValue - op.absValue;
  354. } else if (valType == PC_LENGTH) {
  355. rslt = pcValue - op.pcValue;
  356. } else if (valType == TCOL_LENGTH) {
  357. rslt = tcolValue - op.tcolValue;
  358. }
  359. if (rslt > 0.0) {
  360. return this;
  361. } else {
  362. return op;
  363. }
  364. }
  365. throw new PropertyException("Arguments to max() must have same dimension and value type.");
  366. }
  367. /**
  368. * Return a Numeric which is the minimum of the current value and the
  369. * operand.
  370. * @throws PropertyException If the dimensions or value types of the
  371. * object and the operand are different.
  372. */
  373. public Numeric min(Numeric op) throws PropertyException {
  374. double rslt = 0.0;
  375. // Only compare if have same dimension and value type!
  376. if (dim == op.dim && valType == op.valType && !isMixedType()) {
  377. if (valType == ABS_LENGTH) {
  378. rslt = absValue - op.absValue;
  379. } else if (valType == PC_LENGTH) {
  380. rslt = pcValue - op.pcValue;
  381. } else if (valType == TCOL_LENGTH) {
  382. rslt = tcolValue - op.tcolValue;
  383. }
  384. if (rslt > 0.0) {
  385. return op;
  386. } else {
  387. return this;
  388. }
  389. }
  390. throw new PropertyException("Arguments to min() must have same dimension and value type.");
  391. }
  392. }