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 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /*
  2. * $Id$
  3. * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
  4. * For details on use and redistribution please refer to the
  5. * LICENSE file included with these sources.
  6. */
  7. package org.apache.fop.fo.expr;
  8. import java.util.Vector;
  9. import org.apache.fop.fo.Property;
  10. import org.apache.fop.datatypes.Length;
  11. import org.apache.fop.datatypes.FixedLength;
  12. import org.apache.fop.datatypes.PercentLength;
  13. import org.apache.fop.datatypes.LinearCombinationLength;
  14. import org.apache.fop.datatypes.MixedLength;
  15. import org.apache.fop.datatypes.TableColLength;
  16. import org.apache.fop.datatypes.PercentBase;
  17. /**
  18. * Represents a "numeric" value as defined by the XSL FO Specification.
  19. * This consists of one or more kinds of value specifications, from
  20. * absolute numbers (units power of 0) to lengths (unit power of 1),
  21. * relative lengths (ems), percentage lengths.
  22. * A Numeric can be constructed from other Property types representing
  23. * Numbers or Length-type values.
  24. * Numeric provides methods to return Number and Length values based on
  25. * its current value.
  26. * It supports basic arithmetic operations involving Numerics.
  27. */
  28. public class Numeric {
  29. // Bit fields
  30. public static final int ABS_LENGTH = 1; // abs units (or number)
  31. public static final int PC_LENGTH = 2; // Percentage
  32. public static final int TCOL_LENGTH = 4; // Table units
  33. private int valType;
  34. private double absValue;
  35. private double pcValue;
  36. private PercentBase pcBase = null; // base value for PC_LENGTH component
  37. private double tcolValue;
  38. private int dim;
  39. /**
  40. * Construct a Numeric object by specifying one or more components,
  41. * including absolute length, percent length, table units.
  42. * @param valType A combination of bits representing the value types.
  43. * @param absValue The value of a Number or resolved Length value if
  44. * the ABS_LENGTH flag is set.
  45. * @param pcValue The decimal percent value if the PC_LENGTH flag is set
  46. * @param tcolValue The decimal table unit value if the TCOL_LENGTH flag
  47. * is set.
  48. * @param dim The dimension of the value. 0 for a Number, 1 for a Length
  49. * (any type), >1, <0 if Lengths have been multiplied or divided.
  50. * @pcBase The PercentBase object used to calculate an actual value for
  51. * a PC_LENGTH.
  52. */
  53. protected Numeric(int valType, double absValue, double pcValue,
  54. double tcolValue, int dim, PercentBase pcBase) {
  55. this.valType = valType;
  56. this.absValue = absValue;
  57. this.pcValue = pcValue;
  58. this.tcolValue = tcolValue;
  59. this.dim = dim;
  60. this.pcBase = pcBase;
  61. }
  62. /**
  63. * Construct a Numeric object of dimension 0 from a double.
  64. * @param valType A combination of bits representing the value types.
  65. * @param absValue The value of a Number or resolved Length value.
  66. */
  67. /**
  68. * *
  69. * protected Numeric(int valType, double absValue) {
  70. * this.valType = valType;
  71. * this.absValue = absValue;
  72. * }
  73. */
  74. /**
  75. * Construct a Numeric object from a Number.
  76. * @param num The number.
  77. */
  78. public Numeric(Number num) {
  79. this(ABS_LENGTH, num.doubleValue(), 0.0, 0.0, 0, null);
  80. }
  81. /**
  82. * Construct a Numeric object from a Length.
  83. * @param l The Length.
  84. */
  85. public Numeric(FixedLength l) {
  86. this(ABS_LENGTH, (double)l.mvalue(), 0.0, 0.0, 1, null);
  87. }
  88. /**
  89. * Construct a Numeric object from a PercentLength.
  90. * @param pclen The PercentLength.
  91. */
  92. public Numeric(PercentLength pclen) {
  93. this(PC_LENGTH, 0.0, pclen.value(), 0.0, 1, pclen.getBaseLength());
  94. }
  95. /**
  96. * Construct a Numeric object from a TableColLength.
  97. * @param tclen The TableColLength.
  98. */
  99. public Numeric(TableColLength tclen) {
  100. this(TCOL_LENGTH, 0.0, 0.0, tclen.getTableUnits(), 1, null);
  101. }
  102. /**
  103. * Return the current value as a Length if possible. This constructs
  104. * a new Length or Length subclass based on the current value type
  105. * of the Numeric.
  106. * If the stored value has a unit dimension other than 1, null
  107. * is returned.
  108. */
  109. public Length asLength() {
  110. if (dim == 1) {
  111. Vector len = new Vector(3);
  112. if ((valType & ABS_LENGTH) != 0) {
  113. len.add(new FixedLength((int)absValue));
  114. }
  115. if ((valType & PC_LENGTH) != 0) {
  116. len.add(new PercentLength(pcValue, pcBase));
  117. }
  118. if ((valType & TCOL_LENGTH) != 0) {
  119. len.add(new TableColLength(tcolValue));
  120. }
  121. if (len.size() == 1) {
  122. return (Length)len.elementAt(0);
  123. } else {
  124. return new MixedLength(len);
  125. }
  126. } else {
  127. // or throw exception???
  128. // can't make Length if dimension != 1
  129. return null;
  130. }
  131. }
  132. /**
  133. * Return the current value as a Number if possible.
  134. * Calls asDouble().
  135. */
  136. public Number asNumber() {
  137. return asDouble();
  138. }
  139. public Double asDouble() {
  140. if (dim == 0 && valType == ABS_LENGTH) {
  141. return new Double(absValue);
  142. } else {
  143. // or throw exception???
  144. // can't make Number if dimension != 0
  145. return null;
  146. }
  147. }
  148. /**
  149. * Return the current value as a Integer if possible.
  150. * If the unit dimension is 0 and the value type is ABSOLUTE, an Integer
  151. * is returned. Otherwise null is returned. Note: the current value is
  152. * truncated if necessary to make an integer value.
  153. */
  154. /**
  155. * public Integer asInteger() {
  156. * if (dim == 0 && valType==ABS_LENGTH) {
  157. * return new Integer((int)absValue);
  158. * }
  159. * else {
  160. * // or throw exception???
  161. * // can't make Number if dimension != 0
  162. * return null;
  163. * }
  164. * }
  165. */
  166. /**
  167. * Return a boolean value indiciating whether the currently stored
  168. * value consists of different "types" of values (absolute, percent,
  169. * and/or table-unit.)
  170. */
  171. private boolean isMixedType() {
  172. int ntype = 0;
  173. for (int t = valType; t != 0; t = t >> 1) {
  174. if ((t & 1) != 0) {
  175. ++ntype;
  176. }
  177. }
  178. return ntype > 1;
  179. }
  180. /**
  181. * Subtract the operand from the current value and return a new Numeric
  182. * representing the result.
  183. * @param op The value to subtract.
  184. * @return A Numeric representing the result.
  185. * @throws PropertyException If the dimension of the operand is different
  186. * from the dimension of this Numeric.
  187. */
  188. public Numeric subtract(Numeric op) throws PropertyException {
  189. // Check of same dimension
  190. // Add together absolute and table units
  191. // What about percentages??? Treat as colUnits if they can't be
  192. // in same property!
  193. if (dim == op.dim) {
  194. PercentBase npcBase = ((valType & PC_LENGTH) != 0) ? pcBase
  195. : op.pcBase;
  196. // Subtract each type of value
  197. return new Numeric(valType | op.valType, absValue - op.absValue,
  198. pcValue - op.pcValue,
  199. tcolValue - op.tcolValue, dim, npcBase);
  200. } else {
  201. throw new PropertyException("Can't add Numerics of different dimensions");
  202. }
  203. }
  204. /**
  205. * Add the operand from the current value and return a new Numeric
  206. * representing the result.
  207. * @param op The value to add.
  208. * @return A Numeric representing the result.
  209. * @throws PropertyException If the dimension of the operand is different
  210. * from the dimension of this Numeric.
  211. */
  212. public Numeric add(Numeric op) throws PropertyException {
  213. // Check of same dimension
  214. // Add together absolute and table units
  215. // What about percentages??? Treat as colUnits if they can't be
  216. // in same property!
  217. if (dim == op.dim) {
  218. PercentBase npcBase = ((valType & PC_LENGTH) != 0) ? pcBase
  219. : op.pcBase;
  220. // Add each type of value
  221. return new Numeric(valType | op.valType, absValue + op.absValue,
  222. pcValue + op.pcValue,
  223. tcolValue + op.tcolValue, dim, npcBase);
  224. } else {
  225. throw new PropertyException("Can't add Numerics of different dimensions");
  226. }
  227. }
  228. /**
  229. * Multiply the the current value by the operand and return a new Numeric
  230. * representing the result.
  231. * @param op The multiplier.
  232. * @return A Numeric representing the result.
  233. * @throws PropertyException If both Numerics have "mixed" type.
  234. */
  235. public Numeric multiply(Numeric op) throws PropertyException {
  236. // Multiply together absolute units and add dimensions (exponents)
  237. // What about percentages??? Treat as colUnits if they can't be
  238. // in same property!
  239. if (dim == 0) {
  240. // This is a dimensionless quantity, ie. a "Number"
  241. return new Numeric(op.valType, absValue * op.absValue,
  242. absValue * op.pcValue,
  243. absValue * op.tcolValue, op.dim, op.pcBase);
  244. } else if (op.dim == 0) {
  245. double opval = op.absValue;
  246. return new Numeric(valType, opval * absValue, opval * pcValue,
  247. opval * tcolValue, dim, pcBase);
  248. } else if (valType == op.valType &&!isMixedType()) {
  249. // Check same relbase and pcbase ???
  250. PercentBase npcBase = ((valType & PC_LENGTH) != 0) ? pcBase
  251. : op.pcBase;
  252. return new Numeric(valType, absValue * op.absValue,
  253. pcValue * op.pcValue,
  254. tcolValue * op.tcolValue, dim + op.dim,
  255. npcBase);
  256. } else {
  257. throw new PropertyException("Can't multiply mixed Numerics");
  258. }
  259. }
  260. /**
  261. * Divide the the current value by the operand and return a new Numeric
  262. * representing the result.
  263. * @param op The divisor.
  264. * @return A Numeric representing the result.
  265. * @throws PropertyException If both Numerics have "mixed" type.
  266. */
  267. public Numeric divide(Numeric op) throws PropertyException {
  268. // Multiply together absolute units and add dimensions (exponents)
  269. // What about percentages??? Treat as colUnits if they can't be
  270. // in same property!
  271. if (dim == 0) {
  272. // This is a dimensionless quantity, ie. a "Number"
  273. return new Numeric(op.valType, absValue / op.absValue,
  274. absValue / op.pcValue,
  275. absValue / op.tcolValue, -op.dim, op.pcBase);
  276. } else if (op.dim == 0) {
  277. double opval = op.absValue;
  278. return new Numeric(valType, absValue / opval, pcValue / opval,
  279. tcolValue / opval, dim, pcBase);
  280. } else if (valType == op.valType &&!isMixedType()) {
  281. PercentBase npcBase = ((valType & PC_LENGTH) != 0) ? pcBase
  282. : op.pcBase;
  283. return new Numeric(valType,
  284. (valType == ABS_LENGTH ? absValue / op.absValue : 0.0),
  285. (valType == PC_LENGTH ? pcValue / op.pcValue : 0.0),
  286. (valType == TCOL_LENGTH ? tcolValue / op.tcolValue : 0.0),
  287. dim - op.dim, npcBase);
  288. } else {
  289. throw new PropertyException("Can't divide mixed Numerics.");
  290. }
  291. }
  292. /**
  293. * Return the absolute value of this Numeric.
  294. * @return A new Numeric object representing the absolute value.
  295. */
  296. public Numeric abs() {
  297. return new Numeric(valType, Math.abs(absValue), Math.abs(pcValue),
  298. Math.abs(tcolValue), dim, pcBase);
  299. }
  300. /**
  301. * Return a Numeric which is the maximum of the current value and the
  302. * operand.
  303. * @throws PropertyException If the dimensions or value types of the
  304. * object and the operand are different.
  305. */
  306. public Numeric max(Numeric op) throws PropertyException {
  307. double rslt = 0.0;
  308. // Only compare if have same dimension and value type!
  309. if (dim == op.dim && valType == op.valType &&!isMixedType()) {
  310. if (valType == ABS_LENGTH) {
  311. rslt = absValue - op.absValue;
  312. } else if (valType == PC_LENGTH) {
  313. rslt = pcValue - op.pcValue;
  314. } else if (valType == TCOL_LENGTH) {
  315. rslt = tcolValue - op.tcolValue;
  316. }
  317. if (rslt > 0.0) {
  318. return this;
  319. } else {
  320. return op;
  321. }
  322. }
  323. throw new PropertyException("Arguments to max() must have same dimension and value type.");
  324. }
  325. /**
  326. * Return a Numeric which is the minimum of the current value and the
  327. * operand.
  328. * @throws PropertyException If the dimensions or value types of the
  329. * object and the operand are different.
  330. */
  331. public Numeric min(Numeric op) throws PropertyException {
  332. double rslt = 0.0;
  333. // Only compare if have same dimension and value type!
  334. if (dim == op.dim && valType == op.valType &&!isMixedType()) {
  335. if (valType == ABS_LENGTH) {
  336. rslt = absValue - op.absValue;
  337. } else if (valType == PC_LENGTH) {
  338. rslt = pcValue - op.pcValue;
  339. } else if (valType == TCOL_LENGTH) {
  340. rslt = tcolValue - op.tcolValue;
  341. }
  342. if (rslt > 0.0) {
  343. return op;
  344. } else {
  345. return this;
  346. }
  347. }
  348. throw new PropertyException("Arguments to min() must have same dimension and value type.");
  349. }
  350. }