You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by je...@apache.org on 2012/02/07 20:26:18 UTC
svn commit: r1241563 [1/2] - in
/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src:
main/java/org/apache/chemistry/opencmis/util/content/
main/java/org/apache/chemistry/opencmis/util/content/fractal/
main/java/org/apache...
Author: jens
Date: Tue Feb 7 19:26:17 2012
New Revision: 1241563
URL: http://svn.apache.org/viewvc?rev=1241563&view=rev
Log:
Add more content types to the ObjectGenerator tool: Html, JPEG-Image
Added:
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/ComplexPoint.java
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/ComplexRectangle.java
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/FractalCalculator.java
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/FractalGenerator.java
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/loremipsum/
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/loremipsum/LoremIpsum.java
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/test/java/org/apache/chemistry/opencmis/util/content/loremipsum/
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/test/java/org/apache/chemistry/opencmis/util/content/loremipsum/LoremIpsumTest.java
Removed:
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/LoreIpsum.java
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/test/java/org/apache/chemistry/opencmis/util/content/LoreIpsumTest.java
Modified:
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/repository/MultiThreadedObjectGenerator.java
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/repository/ObjGenApp.java
chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/repository/ObjectGenerator.java
Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/ComplexPoint.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/ComplexPoint.java?rev=1241563&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/ComplexPoint.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/ComplexPoint.java Tue Feb 7 19:26:17 2012
@@ -0,0 +1,54 @@
+////////////////////////////////////////////////////////////////////////////////
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.chemistry.opencmis.util.content.fractal;
+
+public class ComplexPoint {
+ private double real;
+ private double imaginary;
+
+ public ComplexPoint(double real, double imaginary) {
+ this.real = real;
+ this.imaginary = imaginary;
+ }
+
+ public ComplexPoint() {
+ real = 0.0;
+ imaginary = 0.0;
+ }
+
+ public double getImaginary() {
+ return imaginary;
+ }
+
+ public double getReal() {
+ return real;
+ }
+
+ public void set(ComplexPoint cp) {
+ real = cp.getReal();
+ imaginary = cp.getImaginary();
+ }
+
+ public void set(double cr, double ci) {
+ real = cr;
+ imaginary = ci;
+ }
+}
\ No newline at end of file
Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/ComplexRectangle.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/ComplexRectangle.java?rev=1241563&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/ComplexRectangle.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/ComplexRectangle.java Tue Feb 7 19:26:17 2012
@@ -0,0 +1,91 @@
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.chemistry.opencmis.util.content.fractal;
+
+
+public class ComplexRectangle {
+ private double iMin; // imaginary
+ private double iMax;
+ private double rMin; // real
+ private double rMax;
+
+ public ComplexRectangle(double r1, double r2, double i1, double i2) {
+ set(r1, r2, i1, i2);
+ }
+
+ public ComplexRectangle() {
+ set(0.0, 0.0, 0.0, 0.0);
+ }
+
+ public ComplexRectangle(ComplexRectangle cr) {
+ set(cr);
+ }
+
+ public double getIMin() {
+ return iMin;
+ }
+
+ public double getIMax() {
+ return iMax;
+ }
+
+ public double getRMin() {
+ return rMin;
+ }
+
+ public double getRMax() {
+ return rMax;
+ }
+
+ public double getHeight() {
+ return iMax - iMin;
+ }
+
+ public double getWidth() {
+ return rMax - rMin;
+ }
+
+ public void set(ComplexRectangle cr) {
+ set(cr.getRMin(), cr.getRMax(), cr.getIMin(), cr.getIMax());
+ }
+
+ public void set(ComplexPoint p1, ComplexPoint p2) {
+ set(p1.getReal(), p2.getReal(), p1.getImaginary(), p2.getImaginary());
+ }
+
+ public void set(double r1, double r2, double i1, double i2) {
+ if (r1 > r2) {
+ rMin = r2;
+ rMax = r1;
+ } else {
+ rMin = r1;
+ rMax = r2;
+ }
+ if (i1 > i2) {
+ iMin = i2;
+ iMax = i1;
+ } else {
+ iMin = i1;
+ iMax = i2;
+ }
+ }
+}
\ No newline at end of file
Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/FractalCalculator.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/FractalCalculator.java?rev=1241563&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/FractalCalculator.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/FractalCalculator.java Tue Feb 7 19:26:17 2012
@@ -0,0 +1,152 @@
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.chemistry.opencmis.util.content.fractal;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+
+final class FractalCalculator {
+ private int[] colorMap;
+ private double delta;
+ private double iRangeMax;
+ private double iRangeMin;
+ private int maxIterations;
+ private ComplexRectangle newRect;
+ private int numColors;
+ private int imageHeight;
+ private int imageWidth;
+ private double rRangeMax;
+ private double rRangeMin;
+ // For Julia set:
+ private double cJuliaPointR = 0.0; // Real
+ private double cJuliaPointI = 0.0; // Imaginary
+ boolean useJulia = false;
+
+ public FractalCalculator(ComplexRectangle complRect, int maxIters, int imgWidth, int imgHeight, int[] colMap,
+ ComplexPoint juliaPoint) {
+ maxIterations = maxIters;
+ newRect = complRect;
+ imageWidth = imgWidth;
+ imageHeight = imgHeight;
+ colorMap = colMap;
+ numColors = colorMap.length;
+ rRangeMin = newRect.getRMin();
+ rRangeMax = newRect.getRMax();
+ iRangeMin = newRect.getIMin();
+ iRangeMax = newRect.getIMax();
+ delta = (rRangeMax - rRangeMin) / imageWidth;
+ if (null != juliaPoint) {
+ cJuliaPointR = juliaPoint.getReal();
+ cJuliaPointI = juliaPoint.getImaginary();
+ useJulia = true;
+ }
+ }
+
+ public BufferedImage calcFractal() {
+
+ // Assign a color to every pixel ( x , y ) in the Image, corresponding
+ // to
+ // one point, z, in the imaginary plane ( zr, zi ).
+ BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_3BYTE_BGR);
+
+ // For each pixel...
+ for (int x = 0; x < imageWidth; x++) {
+ for (int y = 0; y < imageHeight; y++) {
+ int color = getColor(x, y);
+ image.setRGB(x, y, color);
+ }
+ }
+ return image;
+ }
+
+ private int getColor(int x, int y) {
+ int c = Color.black.getRGB();
+ double zR = rRangeMin + x * delta;
+ double zI = iRangeMin + (imageHeight - y) * delta;
+
+ // Is the point inside the set?
+ int numIterations;
+ if (useJulia)
+ numIterations = testPointJuliaSet(zR, zI, maxIterations);
+ else
+ numIterations = testPointMandelbrot(zR, zI, maxIterations);
+
+ if (numIterations != 0) {
+ // The point is outside the set. It gets a color based on the number
+ // of iterations it took to know this.
+ int colorNum = (int) (numColors * (1.0 - (float) numIterations / (float) maxIterations));
+ colorNum = (colorNum == numColors) ? 0 : colorNum;
+
+ c = colorMap[colorNum];
+ }
+ return c;
+ }
+
+ private int testPointMandelbrot(double cR, double cI, int maxIterations) {
+ // Is the given complex point, (cR, cI), in the Mandelbrot set?
+ // Use the formula: z <= z*z + c, where z is initially equal to c.
+ // If |z| >= 2, then the point is not in the set.
+ // Return 0 if the point is in the set; else return the number of
+ // iterations it took to decide that the point is not in the set.
+ double zR = cR;
+ double zI = cI;
+
+ for (int i = 1; i <= maxIterations; i++) {
+ // To square a complex number: (a+bi)(a+bi) = a*a - b*b + 2abi
+ double zROld = zR;
+ zR = zR * zR - zI * zI + cR;
+ zI = 2 * zROld * zI + cI;
+
+ // We know that if the distance from z to the origin is >= 2
+ // then the point is out of the set. To avoid a square root,
+ // we'll instead check if the distance squared >= 4.
+ double distSquared = zR * zR + zI * zI;
+ if (distSquared >= 4) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ private int testPointJuliaSet(double zR, double zI, int maxIterations) {
+ // Is the given complex point, (zR, zI), in the Julia set?
+ // Use the formula: z <= z*z + c, where z is the point being tested,
+ // and c is the Julia Set constant.
+ // If |z| >= 2, then the point is not in the set.
+ // Return 0 if the point is in the set; else return the number of
+ // iterations it took to decide that the point is not in the set.
+ for (int i = 1; i <= maxIterations; i++) {
+ double zROld = zR;
+ // To square a complex number: (a+bi)(a+bi) = a*a - b*b + 2abi
+ zR = zR * zR - zI * zI + cJuliaPointR;
+ zI = 2 * zROld * zI + cJuliaPointI;
+ // We know that if the distance from z to the origin is >= 2
+ // then the point is out of the set. To avoid a square root,
+ // we'll instead check if the distance squared >= 4.
+ double distSquared = zR * zR + zI * zI;
+ if (distSquared >= 4) {
+ return i;
+ }
+ }
+ return 0;
+ }
+}
\ No newline at end of file
Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/FractalGenerator.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/FractalGenerator.java?rev=1241563&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/FractalGenerator.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/fractal/FractalGenerator.java Tue Feb 7 19:26:17 2012
@@ -0,0 +1,507 @@
+////////////////////////////////////////////////////////////////////////////////
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * Original code inspired by work from David Lebernight
+ * see: http://www.gui.net/fractal.html
+ * email as requested in original source has been sent,
+ * to david@leberknight.com, but address is invalid (2012-02-07)
+ */
+
+package org.apache.chemistry.opencmis.util.content.fractal;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Random;
+
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
+import javax.imageio.stream.ImageOutputStream;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+public class FractalGenerator {
+ private static final Log LOG = LogFactory.getLog(FractalGenerator.class);
+
+ private final static int ZOOM_STEPS_PER_BATCH = 10;
+ private final static int DEFAULT_MAX_ITERATIONS = 33;
+ private final static ComplexRectangle INITIAL_RECT = new ComplexRectangle(-2.1, 1.1, -1.3, 1.3);
+ private final static ComplexRectangle INITIAL_JULIA_RECT = new ComplexRectangle(-2.0, 2.0, -2.0, 2.0);
+ private final static int INITIAL_ITERATIONS = 33;
+
+ // Color:
+ private Map<String, int[]> colorTable;
+ private final String COLORS_BLACK_AND_WHITE = "black & white";
+ private final String COLORS_BLUE_ICE = "blue ice";
+ private final String COLORS_FUNKY = "funky";
+ private final String COLORS_PASTEL = "pastel";
+ private final String COLORS_PSYCHEDELIC = "psychedelic";
+ private final String COLORS_PURPLE_HAZE = "purple haze";
+ private final String COLORS_RADICAL = "radical";
+ private final String COLORS_RAINBOW = "rainbow";
+ private final String COLORS_RAINBOWS = "rainbows";
+ private final String COLORS_SCINTILLATION = "scintillation";
+ private final String COLORS_WARPED = "warped";
+ private final String COLORS_WILD = "wild";
+ private final String COLORS_ZEBRA = "zebra";
+ private final String[] colorSchemes = {COLORS_BLACK_AND_WHITE, COLORS_BLUE_ICE, COLORS_FUNKY, COLORS_PASTEL,
+ COLORS_PSYCHEDELIC, COLORS_PURPLE_HAZE, COLORS_RADICAL, COLORS_RAINBOW, COLORS_RAINBOWS,
+ COLORS_SCINTILLATION, COLORS_WARPED, COLORS_WILD, COLORS_ZEBRA};
+ private final int imageHeight = 512; // default
+ private final int imageWidth = 512; // default
+ private final int numColors = 512; // colors per colormap
+ private FractalCalculator calculator;
+ private int previousIterations = 1;
+ private int maxIterations;
+ String color;
+ int counter = 0;
+ int newRowTile, newColTile;
+ int parts = 16;
+ private int stepInBatch = 0;
+ ComplexRectangle rect;
+ ComplexPoint juliaPoint;
+
+ public FractalGenerator() {
+ reset();
+ }
+
+ private void reset() {
+ rect = new ComplexRectangle(-1.6, -1.2, -0.1, 0.1);
+ juliaPoint = null; // new ComplexPoint();
+ maxIterations = DEFAULT_MAX_ITERATIONS;
+
+ Random ran = new Random();
+ color = colorSchemes[ran.nextInt(colorSchemes.length)];
+ parts = ran.nextInt(30)+3;
+ LOG.debug("Parts: " + parts);
+ maxIterations = DEFAULT_MAX_ITERATIONS;
+ LOG.debug("Original rect " + ": (" + rect.getRMin() + "r," + rect.getRMax() +
+ "r, " + rect.getIMin() + "i, " + rect.getIMax() + "i)");
+ randomizeRect(rect);
+ }
+
+ public ByteArrayOutputStream generateFractal() throws IOException {
+ ByteArrayOutputStream bos = null;
+
+ if (stepInBatch == ZOOM_STEPS_PER_BATCH) {
+ stepInBatch = 0;
+ reset();
+ }
+
+ ++stepInBatch;
+ LOG.debug("Generating rect no " + stepInBatch + ": (" + rect.getRMin() + "r," +
+ rect.getRMax() + "r, " + rect.getIMin() + "i, " + rect.getIMax() + "i)");
+ LOG.debug(" width: " + rect.getWidth() + " height: " + rect.getHeight());
+ bos = genFractal(rect, juliaPoint);
+
+ double r1New = rect.getWidth() * newColTile / parts + rect.getRMin();
+ double r2New = rect.getWidth() * (newColTile+1) / parts + rect.getRMin();
+ double i1New = rect.getIMax() - (rect.getHeight() * newRowTile / parts);
+ double i2New = rect.getIMax() - (rect.getHeight() * (newRowTile+1) / parts);
+ rect.set(r1New, r2New, i1New, i2New);
+ LOG.debug("Done generating fractals.");
+
+ return bos;
+ }
+
+ private void randomizeRect( ComplexRectangle rect) {
+ double jitterFactor = 0.15; // +/- 15%
+ double ran = Math.random() * jitterFactor + (1.0 - jitterFactor);
+ double width = rect.getWidth() * ran;
+ ran = Math.random() * jitterFactor + (1.0 - jitterFactor);
+ double height = rect.getHeight() * ran;
+ ran = Math.random() * jitterFactor + (1.0 - jitterFactor);
+ double r1 = (rect.getWidth() - width) * ran + rect.getRMin();
+ ran = Math.random() * jitterFactor + (1.0 - jitterFactor);
+ double i1 = (rect.getHeight() - height) * ran + rect.getIMin();
+ rect.set(r1, r1+width, i1, i1+height);
+ }
+
+ /**
+ * Create a fractal image as JPEG in memory and return it
+ * @param rect
+ * rectangle of mandelbrot or julia set
+ * @param juliaPoint
+ * point in Julia set or null
+ * @return
+ * byte array with JPEG stream
+ * @throws IOException
+ */
+ public ByteArrayOutputStream genFractal(ComplexRectangle rect, ComplexPoint juliaPoint) throws IOException {
+
+ boolean isJulia = null != juliaPoint;
+ expandRectToFitImage(rect);
+ initializeColors();
+
+ maxIterations = maybeGuessMaxIterations(maxIterations, rect, isJulia);
+ LOG.debug("using " + maxIterations + " iterations.");
+ detectDeepZoom(rect);
+
+ calculator = new FractalCalculator(rect, maxIterations, imageWidth, imageHeight, getCurrentColorMap(),
+ juliaPoint);
+ BufferedImage image = calculator.calcFractal();
+
+ findNewRect(image);
+
+ // fast method to write to a file with default options
+ // ImageIO.write((BufferedImage)(image), "jpg", new File("fractal-" + counter++ + ".jpg"));
+
+ // create image in memory
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(200*1024);
+ ImageOutputStream ios = ImageIO.createImageOutputStream(bos);
+ Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName( "jpg" );
+ ImageWriter imageWriter = writers.next();
+
+ JPEGImageWriteParam params = new JPEGImageWriteParam( Locale.getDefault() );
+ params.setCompressionMode( ImageWriteParam.MODE_EXPLICIT );
+ params.setCompressionQuality( 0.9f );
+
+ imageWriter.setOutput( ios );
+ imageWriter.write( null, new IIOImage( image, null, null ), params );
+ ios.close();
+
+ // write memory block to a file
+ // String fileName = String.format(pattern, counter++);
+ // FileOutputStream outputStream = new FileOutputStream (fileName);
+ // bos.writeTo(outputStream);
+ // bos.close();
+ // outputStream.close();
+
+ return bos;
+ }
+
+ protected int[] getCurrentColorMap() {
+ return colorTable.get(getColor());
+ }
+
+ protected String getColor() {
+ return color;
+ }
+
+ protected void expandRectToFitImage(ComplexRectangle complexRect) {
+ // The complex rectangle must be scaled to fit the pixel image view.
+ // Method: compare the width/height ratios of the two rectangles.
+ double imageWHRatio = 1.0;
+ double complexWHRatio = 1.0;
+ double iMin = complexRect.getIMin();
+ double iMax = complexRect.getIMax();
+ double rMin = complexRect.getRMin();
+ double rMax = complexRect.getRMax();
+ double complexWidth = rMax - rMin;
+ double complexHeight = iMax - iMin;
+
+ if ((imageWidth != 0) && (imageHeight != 0)) {
+ imageWHRatio = ((double) imageWidth / (double) imageHeight);
+ } else
+ return;
+
+ if ((complexWidth != 0) && (complexHeight != 0)) {
+ complexWHRatio = complexWidth / complexHeight;
+ } else
+ return;
+
+ if (imageWHRatio == complexWHRatio)
+ return;
+
+ if (imageWHRatio < complexWHRatio) {
+ // Expand vertically
+ double newHeight = complexWidth / imageWHRatio;
+ double heightDifference = Math.abs(newHeight - complexHeight);
+ iMin = iMin - heightDifference / 2;
+ iMax = iMax + heightDifference / 2;
+ } else {
+ // Expand horizontally
+ double newWidth = complexHeight * imageWHRatio;
+ double widthDifference = Math.abs(newWidth - complexWidth);
+ rMin = rMin - widthDifference / 2;
+ rMax = rMax + widthDifference / 2;
+ }
+ complexRect.set(rMin, rMax, iMin, iMax);
+ }
+
+ private int guessNewMaxIterations(ComplexRectangle cr, boolean isJulia) {
+ // The higher the zoom factor, the more iterations that are needed to
+ // see
+ // the detail. Guess at a number to produce a cool looking fractal:
+ double zoom = INITIAL_RECT.getWidth() / cr.getWidth();
+ if (zoom < 1.0) {
+ zoom = 1.0; // forces logZoom >= 0
+ }
+ double logZoom = Math.log(zoom);
+ double magnitude = (logZoom / 2.3) - 2.0; // just a guess.
+ if (magnitude < 1.0) {
+ magnitude = 1.0;
+ }
+ double iterations = INITIAL_ITERATIONS * (magnitude * logZoom + 1.0);
+ if (isJulia)
+ iterations *= 2.0; // Julia sets tend to need more iterations.
+ return (int) iterations;
+ }
+
+ private int maybeGuessMaxIterations(int maxIterations, ComplexRectangle cr, boolean isJulia) {
+ // If the user did not change the number of iterations, make a guess...
+ if (previousIterations == maxIterations) {
+ maxIterations = guessNewMaxIterations(cr, isJulia);
+ }
+ previousIterations = maxIterations;
+ return maxIterations;
+ }
+
+ private boolean detectDeepZoom(ComplexRectangle cr) {
+ // "Deep Zoom" occurs when the precision provided by the Java type
+ // double
+ // runs out of resolution. The use of BigDecimal is required to fix
+ // this.
+ double deltaDiv2 = cr.getWidth() / ((imageWidth) * 2.0);
+ String min = "" + (cr.getRMin());
+ String minPlus = "" + (cr.getRMin() + deltaDiv2);
+
+ if (Double.valueOf(min).doubleValue() == Double.valueOf(minPlus).doubleValue()) {
+ LOG.warn("Deep Zoom... Drawing resolution will be degraded ;-(");
+ return true;
+ }
+ return false;
+ }
+
+ private void initializeColors() {
+ colorTable = new HashMap<String, int[]>();
+
+ int red = 255;
+ int green = 255;
+ int blue = 255;
+
+ float hue = (float) 1.0;
+ float saturation = (float) 1.0;
+ float brightness = (float) 1.0;
+
+ // COLORS_BLACK_AND_WHITE:
+ int[] colorMap = new int[numColors];
+ for (int colorNum = numColors - 1; colorNum >= 0; colorNum--) {
+ colorMap[colorNum] = Color.white.getRGB();
+ }
+ colorTable.put(COLORS_BLACK_AND_WHITE, colorMap);
+
+ // COLORS_BLUE_ICE:
+ blue = 255;
+ colorMap = new int[numColors];
+ for (int colorNum = numColors - 1; colorNum >= 0; colorNum--) {
+ red = (int) ((255 * (float) colorNum / numColors)) % 255;
+ green = (int) ((255 * (float) colorNum / numColors)) % 255;
+ colorMap[colorNum] = new Color(red, green, blue).getRGB();
+ }
+ colorTable.put(COLORS_BLUE_ICE, colorMap);
+
+ // COLORS_FUNKY:
+ colorMap = new int[numColors];
+ for (int colorNum = numColors - 1; colorNum >= 0; colorNum--) {
+ red = (int) ((1024 * (float) colorNum / numColors)) % 255;
+ green = (int) ((512 * (float) colorNum / numColors)) % 255;
+ blue = (int) ((256 * (float) colorNum / numColors)) % 255;
+ colorMap[numColors - colorNum - 1] = new Color(red, green, blue).getRGB();
+ }
+ colorTable.put(COLORS_FUNKY, colorMap);
+
+ // COLORS_PASTEL
+ brightness = (float) 1.0;
+ colorMap = new int[numColors];
+ for (int colorNum = 0; colorNum < numColors; colorNum++) {
+ hue = ((float) (colorNum * 4) / (float) numColors) % numColors;
+ saturation = ((float) (colorNum * 2) / (float) numColors) % numColors;
+ colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
+ }
+ colorTable.put(COLORS_PASTEL, colorMap);
+
+ // COLORS_PSYCHEDELIC:
+ saturation = (float) 1.0;
+ colorMap = new int[numColors];
+ for (int colorNum = 0; colorNum < numColors; colorNum++) {
+ hue = ((float) (colorNum * 5) / (float) numColors) % numColors;
+ brightness = ((float) (colorNum * 20) / (float) numColors) % numColors;
+ colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
+ }
+ colorTable.put(COLORS_PSYCHEDELIC, colorMap);
+
+ // COLORS_PURPLE_HAZE:
+ red = 255;
+ blue = 255;
+ colorMap = new int[numColors];
+ for (int colorNum = numColors - 1; colorNum >= 0; colorNum--) {
+ green = (int) ((255 * (float) colorNum / numColors)) % 255;
+ colorMap[numColors - colorNum - 1] = new Color(red, green, blue).getRGB();
+ }
+ colorTable.put(COLORS_PURPLE_HAZE, colorMap);
+
+ // COLORS_RADICAL:
+ saturation = (float) 1.0;
+ colorMap = new int[numColors];
+ for (int colorNum = 0; colorNum < numColors; colorNum++) {
+ hue = ((float) (colorNum * 7) / (float) numColors) % numColors;
+ brightness = ((float) (colorNum * 49) / (float) numColors) % numColors;
+ colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
+ }
+ colorTable.put(COLORS_RADICAL, colorMap);
+
+ // COLORS_RAINBOW:
+ saturation = (float) 1.0;
+ brightness = (float) 1.0;
+ colorMap = new int[numColors];
+ for (int colorNum = 0; colorNum < numColors; colorNum++) {
+ hue = (float) colorNum / (float) numColors;
+ colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
+ }
+ colorTable.put(COLORS_RAINBOW, colorMap);
+
+ // COLORS_RAINBOWS:
+ saturation = (float) 1.0;
+ brightness = (float) 1.0;
+ colorMap = new int[numColors];
+ for (int colorNum = 0; colorNum < numColors; colorNum++) {
+ hue = ((float) (colorNum * 5) / (float) numColors) % numColors;
+ colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
+ }
+ colorTable.put(COLORS_RAINBOWS, colorMap);
+
+ // COLORS_SCINTILLATION
+ brightness = (float) 1.0;
+ saturation = (float) 1.0;
+ colorMap = new int[numColors];
+ for (int colorNum = 0; colorNum < numColors; colorNum++) {
+ hue = ((float) (colorNum * 2) / (float) numColors) % numColors;
+ brightness = ((float) (colorNum * 5) / (float) numColors) % numColors;
+ colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
+ }
+ colorTable.put(COLORS_SCINTILLATION, colorMap);
+
+ // COLORS_WARPED:
+ colorMap = new int[numColors];
+ for (int colorNum = numColors - 1; colorNum >= 0; colorNum--) {
+ red = (int) ((1024 * (float) colorNum / numColors)) % 255;
+ green = (int) ((256 * (float) colorNum / numColors)) % 255;
+ blue = (int) ((512 * (float) colorNum / numColors)) % 255;
+ colorMap[numColors - colorNum - 1] = new Color(red, green, blue).getRGB();
+ }
+ colorTable.put(COLORS_WARPED, colorMap);
+
+ // COLORS_WILD:
+ colorMap = new int[numColors];
+ for (int colorNum = 0; colorNum < numColors; colorNum++) {
+ hue = ((float) (colorNum * 1) / (float) numColors) % numColors;
+ saturation = ((float) (colorNum * 2) / (float) numColors) % numColors;
+ brightness = ((float) (colorNum * 4) / (float) numColors) % numColors;
+ colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
+ }
+ colorTable.put(COLORS_WILD, colorMap);
+
+ // COLORS_ZEBRA:
+ colorMap = new int[numColors];
+ for (int colorNum = 0; colorNum < numColors; colorNum++) {
+ if (colorNum % 2 == 0) {
+ colorMap[colorNum] = Color.white.getRGB();
+ ;
+ } else {
+ colorMap[colorNum] = Color.black.getRGB();
+ ;
+ }
+ }
+ colorTable.put(COLORS_ZEBRA, colorMap);
+ }
+
+
+ private void findNewRect(BufferedImage image) {
+
+ int newWidth = image.getWidth() / parts;
+ int newHeight = image.getHeight() / parts;
+ int i=0, j=0;
+ int noTiles = (image.getWidth() / newWidth) * (image.getHeight() / newHeight); // equals parts but be aware of rounding errors!;
+ double[][] stdDev = new double [noTiles] [3];
+
+ for (int y = 0; y+newHeight <= image.getHeight(); y+=newHeight) {
+ for (int x = 0; x+newWidth <= image.getWidth(); x+=newWidth) {
+ Rectangle subRect = new Rectangle(x, y, newWidth, newHeight);
+ calcStdDev(image, subRect, stdDev[i*parts+j]);
+ ++j;
+ }
+ ++i;
+ j=0;
+ }
+
+ // find tile with greatest std deviation:
+ double max = 0;
+ int index = 0;
+ for (i=0; i<noTiles; i++) {
+ double avg = (stdDev[i][0] + stdDev[i][1] +stdDev[i][2]) / 3;
+ if (avg > max) {
+ index = i;
+ max = avg;
+ }
+ }
+ newRowTile = index / parts;
+ newColTile = index % parts;
+ }
+
+ private void calcStdDev(BufferedImage image, Rectangle rect, double[] stdDev) {
+
+ int sumR = 0, sumG = 0, sumB = 0;
+ long sumSR = 0, sumSG = 0, sumSB = 0;
+
+ for (int x = rect.x; x < rect.x+rect.width; x+=1) {
+ for (int y = rect.y; y < rect.y+rect.height; y+=1) {
+ int pixel = image.getRGB(x, y);
+ byte r, g, b, alpha;
+
+ alpha = (byte) (pixel >>> 24);
+ r = (byte) (pixel >>> 16);
+ g = (byte) (pixel >>> 8);
+ b = (byte) pixel;
+ int red = r & 0xFF;
+ int green = g & 0xFF;
+ int blue = b & 0xFF;
+ sumR +=red;
+ sumG += green;
+ sumB += blue;
+ sumSR +=red*red;
+ sumSG += green*green;
+ sumSB += blue*blue;
+ }
+ }
+ int count = rect.width * rect.height;
+ double mean = 0.0;
+
+ mean = sumR / count;
+ stdDev[0] = Math.sqrt(sumSR/count - (mean * mean));
+ mean = sumG / count;
+ stdDev[1] = Math.sqrt(Math.sqrt(sumSG/count - (mean * mean)));
+ mean = sumB / count;
+ stdDev[2] = Math.sqrt(Math.sqrt(sumSB/count - (mean * mean)));
+ }
+
+}
Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/loremipsum/LoremIpsum.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/loremipsum/LoremIpsum.java?rev=1241563&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/loremipsum/LoremIpsum.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-util/src/main/java/org/apache/chemistry/opencmis/util/content/loremipsum/LoremIpsum.java Tue Feb 7 19:26:17 2012
@@ -0,0 +1,1158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.util.content.loremipsum;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * A generator of lorem ipsum text ported from the Python implementation at
+ * http://code.google.com/p/lorem-ipsum-generator/.
+ * Note: original code licensed under the BSD license
+ *
+ */
+public class LoremIpsum {
+
+ private static class WordLengthPair {
+ public int len1;
+ public int len2;
+
+ public WordLengthPair(int len1, int len2) {
+ this.len1 = len1;
+ this.len2 = len2;
+ }
+
+ public String toString() {
+ return "WordLengthPair: len1: " + len1 + ", len2: " + len2;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == null || other == null)
+ return false;
+ if (other.getClass() != WordLengthPair.class)
+ return false;
+ if (len1 == ((WordLengthPair) other).len1 && len2 == ((WordLengthPair) other).len2)
+ return true;
+ else
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return len1 ^ len2;
+ }
+ }
+
+ /**
+ * Delimiters that end sentences.
+ *
+ * @type {Array.<string>}
+ * @private
+ */
+ private static final String DELIMITERS_SENTENCES[] = { ".", "?", "!" };
+
+ /**
+ * Regular expression for splitting a text into sentences.
+ *
+ * @type {RegExp}
+ * @private
+ */
+ private static final String SENTENCE_SPLIT_REGEX = "[\\.\\?\\!]";
+
+ /**
+ * Delimiters that end words.
+ *
+ * @type {Array.<string>}
+ * @private
+ */
+ private static final String DELIMITERS_WORDS[] = { ".", ",", "?", "!" };
+
+ /**
+ * Regular expression for splitting text into words.
+ *
+ * @type {RegExp}
+ * @private
+ */
+ private static final String WORD_SPLIT_REGEX = "\\s";
+
+ private static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
+ /**
+ * Words that can be used in the generated output. Maps a word-length to a
+ * list of words of that length.
+ *
+ * @type {goog.structs.Map}
+ * @private
+ */
+ private Map<Integer, List<String>> words;
+
+ /**
+ * Chains of three words that appear in the sample text Maps a pair of
+ * word-lengths to a third word-length and an optional piece of trailing
+ * punctuation (for example, a period, comma, etc.).
+ *
+ * @type {goog.structs.Map}
+ * @private
+ */
+ private Map<WordLengthPair, List<WordInfo>> chains;
+
+ /**
+ * Pairs of word-lengths that can appear at the beginning of sentences.
+ *
+ * @type {Array}
+ */
+ private List<WordLengthPair> starts;
+
+ /**
+ * Average sentence length in words.
+ *
+ * @type {number}
+ * @private
+ */
+ private double sentenceMean;
+
+ /**
+ * Sigma (sqrt of Objectiance) for the sentence length in words.
+ *
+ * @type {number}
+ * @private
+ */
+ private double sentenceSigma;
+
+ /**
+ * Average paragraph length in sentences.
+ *
+ * @type {number}
+ * @private
+ */
+ private double paragraphMean;
+
+ /**
+ * Sigma (sqrt of variance) for the paragraph length in sentences.
+ *
+ * @type {number}
+ * @private
+ */
+ private double paragraphSigma;
+
+ /**
+ * Sample that the generated text is based on .
+ *
+ * @type {string}
+ */
+ private String sample = SAMPLE;
+
+ /**
+ * Dictionary of words.
+ *
+ * @type {string}
+ */
+ private String dictionary = DICT;
+
+ /**
+ * Picks a random element of the array.
+ *
+ * @param {Array} array The array to pick from.
+ * @return {*} An element from the array.
+ */
+ private WordInfo randomChoice(WordInfo[] array) {
+ return array[randomInt(array.length)];
+ };
+
+ private String randomChoice(String[] array) {
+ return array[randomInt(array.length)];
+ };
+
+ private int randomInt(int length) {
+ return randomGenerator.nextInt(length);
+ }
+
+ private static class WordInfo {
+ int len;
+ String delim;
+ }
+
+ private Random randomGenerator = new Random();
+
+ /**
+ * Generates random strings of "lorem ipsum" text, based on the word
+ * distribution of a sample text, using the words in a dictionary.
+ *
+ * @constructor
+ */
+ public LoremIpsum() {
+ generateChains(this.sample);
+ generateStatistics(this.sample);
+ initializeDictionary(this.dictionary);
+ };
+
+ public LoremIpsum(String sample, String dictionary) {
+ this.sample = sample;
+ this.dictionary = dictionary;
+ generateChains(this.sample);
+ generateStatistics(this.sample);
+ initializeDictionary(this.dictionary);
+ };
+
+ public LoremIpsum(String sample, String[] dictionary) {
+ this.sample = sample;
+ this.dictionary = null;
+ generateChains(this.sample);
+ generateStatistics(this.sample);
+ initializeDictionary(dictionary);
+ };
+
+ public LoremIpsum(String sample) {
+ this.sample = sample;
+ String[] dictWords = filterNotEmptyOrWhiteSpace(sample.split("[^\\p{L}]"/* "\\W" */)).toArray(new String[0]);
+ Set<String> dict = new HashSet<String>(Arrays.asList(dictWords));
+ dictWords = dict.toArray(new String[0]);
+ Arrays.sort(dictWords);
+
+ generateChains(this.sample);
+ generateStatistics(this.sample);
+ initializeDictionary(dictWords);
+ };
+
+ /**
+ * Generates a single lorem ipsum paragraph, of random length.
+ *
+ * @param {boolean} opt_startWithLorem Whether to start the sentence with
+ * the standard "Lorem ipsum..." first sentence.
+ * @return {string} The generated sentence.
+ */
+ public String generateParagraph(boolean opt_startWithLorem) {
+ // The length of the paragraph is a normally distributed random
+ // Objectiable.
+ Double paragraphLengthDbl = randomNormal(this.paragraphMean, this.paragraphSigma);
+ int paragraphLength = Math.max((int) Math.floor(paragraphLengthDbl), 1);
+
+ // Construct a paragraph from a number of sentences.
+ List<String> paragraph = new ArrayList<String>();
+ boolean startWithLorem = opt_startWithLorem;
+ while (paragraph.size() < paragraphLength) {
+ String sentence = this.generateSentence(startWithLorem);
+ paragraph.add(sentence);
+ startWithLorem = false;
+ }
+
+ StringBuffer result = new StringBuffer();
+ // Form the paragraph into a string.
+ for (String sentence : paragraph) {
+ result.append(sentence);
+ result.append(" ");
+ }
+ return result.toString();
+ }
+
+ /**
+ * Generates a single sentence, of random length.
+ *
+ * @param {boolean} opt_startWithLorem Whether to start the setnence with
+ * the standard "Lorem ipsum..." first sentence.
+ * @return {string} The generated sentence.
+ */
+ public String generateSentence(boolean opt_startWithLorem) {
+ if (this.chains.size() == 0 || this.starts.size() == 0) {
+ throw new RuntimeException("No chains created (Invalid sample text?)");
+ }
+
+ if (this.words.size() == 0) {
+ throw new RuntimeException("No dictionary");
+ }
+
+ // The length of the sentence is a normally distributed random
+ // Objectiable.
+ double sentenceLengthDbl = randomNormal(this.sentenceMean, this.sentenceSigma);
+ int sentenceLength = Math.max((int) Math.floor(sentenceLengthDbl), 1);
+
+ String wordDelimiter = ""; // Defined here in case while loop doesn't
+ // run
+
+ // Start the sentence with "Lorem ipsum...", if desired
+ List<String> sentence;
+ ;
+ if (opt_startWithLorem) {
+ String lorem = "lorem ipsum dolor sit amet, consecteteur adipiscing elit";
+ sentence = new ArrayList<String>(Arrays.asList(splitWords(lorem)));
+ if (sentence.size() > sentenceLength) {
+ sentence.subList(0, sentenceLength);
+ }
+ String lastWord = sentence.get(sentence.size() - 1);
+ String lastChar = lastWord.substring(lastWord.length() - 1);
+ if (contains(DELIMITERS_WORDS, lastChar)) {
+ wordDelimiter = lastChar;
+ }
+ } else {
+ sentence = new ArrayList<String>();
+ }
+
+ WordLengthPair previous = new WordLengthPair(0, 0);
+
+ // Generate a sentence from the "chains"
+ while (sentence.size() < sentenceLength) {
+ // If the current starting point is invalid, choose another randomly
+ if (!this.chains.containsKey(previous)) {
+ previous = this.chooseRandomStart_();
+ }
+
+ // Choose the next "chain" to go to. This determines the next word
+ // length we use, and whether there is e.g. a comma at the end of
+ // the word.
+ WordInfo chain = randomChoice(this.chains.get(previous).toArray(new WordInfo[0]));
+ int wordLength = chain.len;
+
+ // If the word delimiter contained in the chain is also a sentence
+ // delimiter, then we don"t include it because we don"t want the
+ // sentence to end prematurely (we want the length to match the
+ // sentence_length value).
+ if (contains(DELIMITERS_SENTENCES, chain.delim)) {
+ wordDelimiter = "";
+ } else {
+ wordDelimiter = chain.delim;
+ }
+
+ // Choose a word randomly that matches (or closely matches) the
+ // length we're after.
+ int closestLength = chooseClosest(this.words.keySet().toArray(new Integer[0]), wordLength);
+ String word = randomChoice(this.words.get(closestLength).toArray(new String[0]));
+
+ sentence.add(word + wordDelimiter);
+ previous = new WordLengthPair(previous.len2, wordLength);
+
+ }
+
+ // Finish the sentence off with capitalisation, a period and
+ // form it into a string
+ StringBuffer result = new StringBuffer();
+ for (String s : sentence) {
+ result.append(s);
+ result.append(" ");
+ }
+ result.deleteCharAt(result.length() - 1);
+
+ result.replace(0, 1, result.substring(0, 1).toUpperCase());
+ int strLen = result.length() - 1;
+ if (wordDelimiter.length() > 0 && wordDelimiter.charAt(0) == result.charAt(strLen))
+ result.deleteCharAt(strLen);
+ result.append(".");
+ return result.toString();
+ }
+
+ public String getSample() {
+ return sample;
+ }
+
+ public void setSample(String sample) {
+ this.sample = sample;
+ generateChains(this.sample);
+ generateStatistics(this.sample);
+ }
+
+ public String getDictionary() {
+ return dictionary;
+ }
+
+ public void setDictionary(String dictionary) {
+ this.dictionary = dictionary;
+ initializeDictionary(this.dictionary);
+ }
+
+ /**
+ * Generates multiple paragraphs of text, with begin before the paragraphs,
+ * end after the paragraphs, and between between each two paragraphs.
+ */
+ private String generateMarkupParagraphs(String begin, String end, String between, int quantity,
+ boolean startWithLorem) {
+
+ StringBuffer text = new StringBuffer();
+
+ text.append(begin);
+ String para = generateParagraph(startWithLorem);
+ text.append(para);
+ while (text.length() < quantity) {
+ para = generateParagraph(false);
+ text.append(para);
+ if (text.length() < quantity)
+ text.append(between);
+ }
+
+ text.append(end);
+ return text.toString();
+ }
+
+ /**
+ * Generates multiple paragraphs of text, with begin before the paragraphs,
+ * end after the paragraphs, and between between each two paragraphs.
+ * @throws IOException
+ */
+ private void generateMarkupParagraphs(Appendable writer, String begin, String end, String between, int quantity,
+ boolean startWithLorem) throws IOException {
+
+ int len = begin.length();
+ writer.append(begin);
+ String para = generateParagraph(startWithLorem);
+ len += para.length();
+ writer.append(para);
+ while (len < quantity) {
+ para = generateParagraph(false);
+ len += para.length();
+ writer.append(para);
+ if (len < quantity) {
+ writer.append(between);
+ len += para.length();
+ }
+ }
+
+ writer.append(end);
+ }
+ /**
+ * Generates multiple sentences of text, with begin before the sentences,
+ * end after the sentences, and between between each two sentences.
+ */
+ private String generateMarkupSentences(String begin, String end, String between, int quantity,
+ boolean startWithLorem) {
+
+ StringBuffer text = new StringBuffer();
+ text.append(begin);
+ String sentence = generateSentence(startWithLorem);
+ text.append(sentence);
+
+ while (text.length() < quantity) {
+ sentence = generateSentence(false);
+ text.append(sentence);
+ if (text.length() < quantity)
+ text.append(between);
+ }
+
+ text.append(end);
+ return text.toString();
+ }
+
+ /**
+ * Generates the chains and starts values required for sentence generation.
+ *
+ * @param {string} sample The same text.
+ * @private
+ */
+ private void generateChains(String sample) {
+
+ String[] words = splitWords(sample);
+ WordInfo[] wordInfos = generateWordInfo(words);
+ WordLengthPair previous = new WordLengthPair(0, 0);
+ List<WordLengthPair> starts = new ArrayList<WordLengthPair>();
+ List<String> delimList = Arrays.asList(DELIMITERS_SENTENCES);
+ Map<WordLengthPair, List<WordInfo>> chains = new HashMap<WordLengthPair, List<WordInfo>>();
+
+ for (WordInfo wi : wordInfos) {
+ if (wi.len == 0)
+ continue;
+
+ List<WordInfo> value = chains.get(previous);
+ if (null == value)
+ chains.put(previous, new ArrayList<WordInfo>());
+ else
+ chains.get(previous).add(wi);
+
+ if (delimList.contains(wi.delim))
+ starts.add(previous);
+
+ previous.len1 = previous.len2;
+ previous.len2 = wi.len;
+ }
+
+ if (chains.size() > 0) {
+ this.chains = chains;
+ this.starts = starts;
+ } else {
+ throw new RuntimeException("Invalid sample text.");
+ }
+
+ }
+
+ /**
+ * Calculates the mean and standard deviation of sentence and paragraph
+ * lengths.
+ *
+ * @param {string} sample The same text.
+ * @private
+ */
+ private void generateStatistics(String sample) {
+ this.generateSentenceStatistics(sample);
+ this.generateParagraphStatistics(sample);
+ }
+
+ /**
+ * Calculates the mean and standard deviation of the lengths of sentences
+ * (in words) in a sample text.
+ *
+ * @param {string} sample The same text.
+ * @private
+ */
+ private void generateSentenceStatistics(String sample) {
+ List<String> sentences = filterNotEmptyOrWhiteSpace(splitSentences(sample));
+ int sentenceLengths[] = new int[sentences.size()];
+ for (int i = 0; i < sentences.size(); i++) {
+ String[] words = splitWords(sentences.get(i));
+ sentenceLengths[i] = words.length;
+ }
+ this.sentenceMean = mean(sentenceLengths);
+ this.sentenceSigma = sigma(sentenceLengths);
+ }
+
+ /**
+ * Calculates the mean and standard deviation of the lengths of paragraphs
+ * (in sentences) in a sample text.
+ *
+ * @param {string} sample The same text.
+ * @private
+ */
+ private void generateParagraphStatistics(String sample) {
+ List<String> paragraphs = filterNotEmptyOrWhiteSpace(splitParagraphs(sample));
+
+ int paragraphLengths[] = new int[paragraphs.size()];
+ for (int i = 0; i < paragraphs.size(); i++) {
+ String[] sentences = splitSentences(paragraphs.get(i));
+ paragraphLengths[i] = sentences.length;
+ }
+
+ this.paragraphMean = mean(paragraphLengths);
+ this.paragraphSigma = sigma(paragraphLengths);
+ }
+
+ /**
+ * Sets the generator to use a given selection of words for generating
+ * sentences with.
+ *
+ * @param {string} dictionary The dictionary to use.
+ */
+ private void initializeDictionary(String dictionary) {
+ String[] dictionaryWords = splitWords(dictionary);
+ initializeDictionary(dictionaryWords);
+ }
+
+ private void initializeDictionary(String[] dictionaryWords) {
+ words = new HashMap<Integer, List<String>>();
+ for (String word : dictionaryWords) {
+ List<String> wordWithLen = words.get(word.length());
+ if (null == wordWithLen) {
+ List<String> list = new ArrayList<String>();
+ list.add(word);
+ words.put(word.length(), list);
+ } else {
+ wordWithLen.add(word);
+ }
+ }
+
+ if (words.size() == 0)
+ throw new RuntimeException("Invalid dictionary.");
+ }
+
+ /**
+ * Picks a random starting chain.
+ *
+ * @return {string} The starting key.
+ * @private
+ */
+ private WordLengthPair chooseRandomStart_() {
+ Set<WordLengthPair> keys = chains.keySet();
+ Set<WordLengthPair> validStarts = new HashSet<WordLengthPair>(starts);
+ validStarts.retainAll(keys);
+ int index = randomInt(validStarts.size());
+ WordLengthPair wlPair = validStarts.toArray(new WordLengthPair[0])[index];
+ return wlPair;
+ }
+
+ /**
+ * Splits a piece of text into paragraphs.
+ *
+ * @param {string} text The text to split.
+ * @return {Array.<string>} An array of paragraphs.
+ * @private
+ */
+
+ static String[] splitParagraphs(String text) {
+ return filterNotEmptyOrWhiteSpace(text.split("\n")).toArray(new String[0]);
+ }
+
+ /**
+ * Splits a piece of text into sentences.
+ *
+ * @param {string} text The text to split.
+ * @return {Array.<string>} An array of sentences.
+ * @private
+ */
+ static String[] splitSentences(String text) {
+ return filterNotEmptyOrWhiteSpace(text.split(SENTENCE_SPLIT_REGEX)).toArray(new String[0]);
+ }
+
+ /**
+ * Splits a piece of text into words..
+ *
+ * @param {string} text The text to split.
+ * @return {Array.<string>} An array of words.
+ * @private
+ */
+ static String[] splitWords(String text) {
+ return filterNotEmptyOrWhiteSpace(text.split(WORD_SPLIT_REGEX)).toArray(new String[0]);
+ }
+
+ /**
+ * Find the number in the list of values that is closest to the target.
+ *
+ * @param {Array.<number>} values The values.
+ * @param {number} target The target value.
+ * @return {number} The closest value.
+ */
+ static int chooseClosest(Integer[] values, int target) {
+ int closest = values[0];
+ for (int value : values) {
+ if (Math.abs(target - value) < Math.abs(target - closest))
+ closest = value;
+ }
+
+ return closest;
+ }
+
+ /**
+ * Gets info about a word used as part of the lorem ipsum algorithm.
+ *
+ * @param {string} word The word to check.
+ * @return {Array} A two element array. The first element is the size of the
+ * word. The second element is the delimiter used in the word.
+ * @private
+ */
+ static private WordInfo getWordInfo(String word) {
+ WordInfo ret = new WordInfo();
+ for (String delim : DELIMITERS_WORDS) {
+ if (word.endsWith(delim)) {
+ ret.len = word.length() - delim.length();
+ ret.delim = delim;
+ return ret;
+ }
+ }
+ ret.len = word.length();
+ ret.delim = "";
+ return ret;
+ }
+
+ static private WordInfo[] generateWordInfo(String[] words) {
+ WordInfo[] result = new WordInfo[words.length];
+ int i = 0;
+ for (String word : words)
+ result[i++] = getWordInfo(word);
+ return result;
+ }
+
+ /**
+ * Constant used for {@link #randomNormal_}.
+ *
+ * @type {number}
+ * @private
+ */
+ private static final double NV_MAGICCONST_ = 4 * Math.exp(-0.5) / Math.sqrt(2.0);
+
+ /**
+ * Generates a random number for a normal distribution with the specified
+ * mean and sigma.
+ *
+ * @param {number} mu The mean of the distribution.
+ * @param {number} sigma The sigma of the distribution.
+ * @private
+ */
+ private static double randomNormal(double mu, double sigma) {
+ double z = 0.0d;
+ while (true) {
+ double u1 = Math.random();
+ double u2 = 1.0d - Math.random();
+ z = NV_MAGICCONST_ * (u1 - 0.5d) / u2;
+ double zz = z * z / 4.0d;
+ if (zz <= -Math.log(u2)) {
+ break;
+ }
+ }
+ return mu + z * sigma;
+ }
+
+ /**
+ * Returns the text if it is not empty or just whitespace.
+ *
+ * @param {string} text the text to check.
+ * @return {boolean} Whether the text is neither empty nor whitespace.
+ * @private
+ */
+ private static List<String> filterNotEmptyOrWhiteSpace(String[] arr) {
+ List<String> result = new ArrayList<String>();
+ for (String s : arr) {
+ String trims = s.trim();
+ if (trims.length() > 0)
+ result.add(trims);
+ }
+ return result;
+ }
+
+ public static double mean(int[] values) {
+ return ((double) sum(values)) / ((double) (Math.max(values.length, 1)));
+ }
+
+ public static double mean(double[] values) {
+ return sum(values) / ((double) (Math.max(values.length, 1)));
+ }
+
+ public static double variance(double[] values) {
+ double[] squared = new double[values.length];
+ for (int i = 0; i < values.length; i++)
+ squared[i] = values[i] * values[i];
+
+ double meanVal = mean(values);
+ return mean(squared) - (meanVal * meanVal);
+ }
+
+ public static double sigma(int[] values) {
+ double[] d = new double[values.length];
+ for (int i = 0; i < values.length; i++)
+ d[i] = (double) values[i];
+
+ return sigma(d);
+ }
+
+ public static double sigma(double[] values) {
+ return Math.sqrt(variance(values));
+ }
+
+ public static int sum(int[] values) {
+ int sum = 0;
+ for (int val : values)
+ sum += val;
+ return sum;
+ }
+
+ public static double sum(double[] values) {
+ double sum = 0.0d;
+ for (double val : values)
+ sum += val;
+ return sum;
+ }
+
+ public static boolean contains(String[] array, String val) {
+ for (String s : array)
+ if (s.equals(val))
+ return true;
+ return false;
+ }
+
+ /* for unit testing */
+ double getSentenceMean() {
+ return sentenceMean;
+ }
+
+ double getSentenceSigma() {
+ return sentenceSigma;
+ }
+
+ double getParagraphMean() {
+ return paragraphMean;
+ }
+
+ double getParagraphSigma() {
+ return paragraphSigma;
+ }
+
+ /**
+ * Dictionary of words for lorem ipsum.
+ *
+ * @type {string}
+ * @private
+ */
+ private static final String DICT = "a ac accumsan ad adipiscing aenean aliquam aliquet amet ante "
+ + "aptent arcu at auctor augue bibendum blandit class commodo "
+ + "condimentum congue consectetuer consequat conubia convallis cras "
+ + "cubilia cum curabitur curae cursus dapibus diam dictum dictumst "
+ + "dignissim dis dolor donec dui duis egestas eget eleifend elementum "
+ + "elit eni enim erat eros est et etiam eu euismod facilisi facilisis "
+ + "fames faucibus felis fermentum feugiat fringilla fusce gravida "
+ + "habitant habitasse hac hendrerit hymenaeos iaculis id imperdiet "
+ + "in inceptos integer interdum ipsum justo lacinia lacus laoreet "
+ + "lectus leo libero ligula litora lobortis lorem luctus maecenas "
+ + "magna magnis malesuada massa mattis mauris metus mi molestie "
+ + "mollis montes morbi mus nam nascetur natoque nec neque netus "
+ + "nibh nisi nisl non nonummy nostra nulla nullam nunc odio orci "
+ + "ornare parturient pede pellentesque penatibus per pharetra "
+ + "phasellus placerat platea porta porttitor posuere potenti praesent "
+ + "pretium primis proin pulvinar purus quam quis quisque rhoncus "
+ + "ridiculus risus rutrum sagittis sapien scelerisque sed sem semper "
+ + "senectus sit sociis sociosqu sodales sollicitudin suscipit "
+ + "suspendisse taciti tellus tempor tempus tincidunt torquent tortor "
+ + "tristique turpis ullamcorper ultrices ultricies urna ut Objectius ve "
+ + "vehicula vel velit venenatis vestibulum vitae vivamus viverra " + "volutpat vulputate";
+
+ /**
+ * A sample to use for generating the distribution of word and sentence
+ * lengths in lorem ipsum.
+ *
+ * @type {string}
+ * @private
+ */
+ private static final String SAMPLE = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean "
+ + "commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus "
+ + "et magnis dis parturient montes, nascetur ridiculus mus. Donec quam "
+ + "felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla "
+ + "consequat massa quis enim. Donec pede justo, fringilla vel, aliquet "
+ + "nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, "
+ + "venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. "
+ + "Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean "
+ + "vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat "
+ + "vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra "
+ + "quis, feugiat a, tellus. Phasellus viverra nulla ut metus Objectius "
+ + "laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel "
+ + "augue. Curabitur ullamcorper ultricies nisi. Nam eget dui.\n\n" +
+
+ "Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem "
+ + "quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam "
+ + "nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec "
+ + "odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis "
+ + "faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus "
+ + "tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales "
+ + "sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit "
+ + "cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend "
+ + "sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, "
+ + "metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis "
+ + "hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci "
+ + "luctus et ultrices posuere cubilia Curae; In ac dui quis mi " + "consectetuer lacinia.\n\n" +
+
+ "Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet "
+ + "nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer "
+ + "ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent "
+ + "adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy "
+ + "metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros "
+ + "et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, "
+ + "nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit "
+ + "risus. Phasellus nec sem in justo pellentesque facilisis. Etiam "
+ + "imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus "
+ + "non, auctor et, hendrerit quis, nisi.\n\n" +
+
+ "Curabitur ligula sapien, tincidunt non, euismod vitae, posuere "
+ + "imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed "
+ + "cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus "
+ + "accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci "
+ + "luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis "
+ + "porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis "
+ + "orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, "
+ + "bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede "
+ + "sit amet augue. In turpis. Pellentesque posuere. Praesent turpis.\n\n" +
+
+ "Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu "
+ + "sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales "
+ + "nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse "
+ + "pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, "
+ + "nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in "
+ + "faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id "
+ + "purus. Ut Objectius tincidunt libero. Phasellus dolor. Maecenas vestibulum "
+ + "mollis diam. Pellentesque ut neque. Pellentesque habitant morbi "
+ + "tristique senectus et netus et malesuada fames ac turpis egestas.\n\n" +
+
+ "In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac "
+ + "felis quis tortor malesuada pretium. Pellentesque auctor neque nec "
+ + "urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean "
+ + "viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et "
+ + "netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis "
+ + "pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna.\n\n" +
+
+ "In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare "
+ + "lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis "
+ + "ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. "
+ + "Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, "
+ + "quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at "
+ + "pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo "
+ + "quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam "
+ + "sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce "
+ + "risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis " + "vulputate lorem.\n\n" +
+
+ "Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, "
+ + "dui et placerat feugiat, eros pede Objectius nisi, condimentum viverra "
+ + "felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, "
+ + "Objectius ut, felis. In auctor lobortis lacus. Quisque libero metus, "
+ + "condimentum nec, tempor a, commodo mollis, magna. Vestibulum "
+ + "ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia "
+ + "erat. Praesent blandit laoreet nibh.\n\n" +
+
+ "Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, "
+ + "neque sit amet convallis pulvinar, justo nulla eleifend augue, ac "
+ + "auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. "
+ + "Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. "
+ + "Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In "
+ + "hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis "
+ + "mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat "
+ + "nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, " + "lacus.\n\n" +
+
+ "Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, "
+ + "dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi "
+ + "congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin "
+ + "fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit "
+ + "amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam "
+ + "gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac "
+ + "sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus "
+ + "blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in "
+ + "libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In "
+ + "consectetuer turpis ut velit. Nulla sit amet est. Praesent metus "
+ + "tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus "
+ + "ipsum, faucibus ut, ullamcorper id, Objectius ac, leo. Suspendisse "
+ + "feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum "
+ + "nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac "
+ + "massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, "
+ + "iaculis quis, molestie non, velit.\n\n" +
+
+ "Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. "
+ + "Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus "
+ + "at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet "
+ + "velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. "
+ + "Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, "
+ + "sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla "
+ + "facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere "
+ + "iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. "
+ + "Curabitur suscipit suscipit tellus.\n\n" +
+
+ "Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id "
+ + "nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu "
+ + "pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante "
+ + "odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque "
+ + "suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce "
+ + "ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu "
+ + "quis ligula mattis placerat. Duis lobortis massa imperdiet quam. " + "Suspendisse potenti.\n\n" +
+
+ "Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, "
+ + "lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat "
+ + "volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam "
+ + "eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris "
+ + "ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta "
+ + "dolor. Class aptent taciti sociosqu ad litora torquent per conubia "
+ + "nostra, per inceptos hymenaeos.\n\n" +
+
+ "Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. "
+ + "Sed a libero. Cras Objectius. Donec vitae orci sed dolor rutrum auctor. "
+ + "Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, "
+ + "elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum "
+ + "sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus "
+ + "non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. " + "Vestibulum eu odio.\n\n" +
+
+ "Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. "
+ + "Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique "
+ + "sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse "
+ + "faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, "
+ + "vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce "
+ + "fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae "
+ + "odio lacinia tincidunt. Praesent ut ligula non mi Objectius sagittis. "
+ + "Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus "
+ + "consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna "
+ + "cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit "
+ + "quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar " + "varius.\n\n";
+
+ /**
+ * Generates a number of paragraphs, with each paragraph surrounded by HTML
+ * pararaph tags.
+ *
+ * @param quantity
+ * @param startWithLorem
+ * @return
+ */
+ public String generateParagraphsHtml(int quantity, boolean startWithLorem) {
+
+ return generateMarkupParagraphs("<p>" + LINE_SEPARATOR + "\t", LINE_SEPARATOR + "</p>", LINE_SEPARATOR + "</p>"
+ + LINE_SEPARATOR + "<p>" + LINE_SEPARATOR + "\t", quantity, startWithLorem);
+
+ }
+
+ /**
+ * Generates a number of paragraphs, with each paragraph surrounded by HTML
+ * pararaph tags.
+ *
+ * @param writer
+ * @param quantity
+ * @param startWithLorem
+ * @throws IOException
+ */
+ public void generateParagraphsHtml(Appendable writer, int quantity, boolean startWithLorem) throws IOException {
+
+ generateMarkupParagraphs(writer, "<p>" + LINE_SEPARATOR + "\t", LINE_SEPARATOR + "</p>", LINE_SEPARATOR + "</p>"
+ + LINE_SEPARATOR + "<p>" + LINE_SEPARATOR + "\t", quantity, startWithLorem);
+
+ }
+
+
+ /**
+ * Generates one paragraph of HTML, surrounded by HTML pararaph tags.
+ *
+ * @param quantity
+ * @param startWithLorem
+ * @return
+ */
+ public String generateOneParagraphHtml(int quantity, boolean startWithLorem) {
+
+ return generateMarkupSentences("<p>" + LINE_SEPARATOR + "\t", LINE_SEPARATOR + "</p>", LINE_SEPARATOR,
+ quantity, startWithLorem);
+
+ }
+
+ /**
+ * Generates a number of paragraphs, with each paragraph surrounded by HTML
+ * paragraph tags as a full HTML page.
+ *
+ * @param quantity
+ * @param startWithLorem
+ * @return
+ */
+ public String generateParagraphsFullHtml(int quantity, boolean startWithLorem) {
+
+ String prefix = "<html>" + LINE_SEPARATOR + "<header>" + LINE_SEPARATOR + "<title>Lorem Ipsum</title>"
+ + LINE_SEPARATOR + "</header>" + LINE_SEPARATOR + LINE_SEPARATOR + "<body>";
+ String postfix = "</body>" + LINE_SEPARATOR + "</html>" + LINE_SEPARATOR;
+
+ return generateMarkupParagraphs(prefix + LINE_SEPARATOR + "<p>" + LINE_SEPARATOR + "\t", LINE_SEPARATOR
+ + "</p>" + LINE_SEPARATOR + postfix, LINE_SEPARATOR + "</p>" + LINE_SEPARATOR + "<p>" + LINE_SEPARATOR
+ + "\t", quantity, startWithLorem);
+ }
+
+
+ /**
+ * Generates a number of paragraphs, with each paragraph surrounded by HTML
+ * paragraph tags as a full HTML page.
+ *
+ * @param writer
+ * @param quantity
+ * @param startWithLorem
+ * @throws IOException
+ */
+ public void generateParagraphsFullHtml(Appendable writer, int quantity, boolean startWithLorem) throws IOException {
+
+ String prefix = "<html>" + LINE_SEPARATOR + "<header>" + LINE_SEPARATOR + "<title>Lorem Ipsum</title>"
+ + LINE_SEPARATOR + "</header>" + LINE_SEPARATOR + LINE_SEPARATOR + "<body>";
+ String postfix = "</body>" + LINE_SEPARATOR + "</html>" + LINE_SEPARATOR;
+
+ generateMarkupParagraphs(writer, prefix + LINE_SEPARATOR + "<p>" + LINE_SEPARATOR + "\t", LINE_SEPARATOR
+ + "</p>" + LINE_SEPARATOR + postfix, LINE_SEPARATOR + "</p>" + LINE_SEPARATOR + "<p>" + LINE_SEPARATOR
+ + "\t", quantity, startWithLorem);
+ }
+
+
+ /**
+ * Generates a number of paragraphs, with each paragraph separated by two
+ * newlines.
+ *
+ * @param quantity
+ * @param startWithLorem
+ * @return
+ */
+ public String generateParagraphsPlainText(int quantity, boolean startWithLorem) {
+
+ return generateMarkupParagraphs("", "", LINE_SEPARATOR + LINE_SEPARATOR, quantity, startWithLorem);
+ }
+
+ /**
+ * Generates a number of paragraphs, with each paragraph separated by two
+ * newlines.
+ *
+ * @param writer
+ * @param quantity
+ * @param startWithLorem
+ * @throws IOException
+ */
+ public void generateParagraphsPlainText(Appendable writer, int quantity, boolean startWithLorem) throws IOException {
+
+ generateMarkupParagraphs(writer, "", "", LINE_SEPARATOR + LINE_SEPARATOR, quantity, startWithLorem);
+ }
+
+ /**
+ * Generates a number of paragraphs, with each paragraph separated by two
+ * newlines and no line exceeding maxCols columns
+ *
+ * @param quantity
+ * @param startWithLorem
+ * @return
+ */
+ public String generateParagraphsPlainText(int quantity, int maxCols, boolean startWithLorem) {
+
+ StringWriter writer = new StringWriter(quantity + 512);
+ try {
+ generateParagraphsPlainText(writer, quantity, maxCols, startWithLorem);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return writer.toString();
+ }
+
+ /**
+ * Generates a number of paragraphs, with each paragraph separated by two
+ * newlines and no line exceeding maxCols columns
+ *
+ * @param writer
+ * @param quantity
+ * @param startWithLorem
+ * @throws IOException
+ */
+ public void generateParagraphsPlainText(Appendable writer, int quantity, int maxCols, boolean startWithLorem) throws IOException {
+
+ String delims = " .,?!";
+ String unformatted = generateMarkupParagraphs("", "", LINE_SEPARATOR + LINE_SEPARATOR, quantity, startWithLorem);
+ int len = unformatted.length();
+
+ if (maxCols <= 0)
+ writer.append(unformatted);
+ else {
+ int startPos = 0;
+ while (startPos < len - 1) {
+ int endPos = Math.min(startPos + maxCols, len - 1);
+ boolean shift = true;
+ // check if there is already a line break:
+ for (int i = startPos; i < endPos; i++) {
+ if (unformatted.charAt(i) == '\n') {
+ shift = false;
+ endPos = i;
+ }
+ }
+ char ch = unformatted.charAt(endPos);
+ while (shift) {
+ for (int i = 0; i < delims.length(); i++) {
+ if (ch == delims.charAt(i)) {
+ shift = false;
+ break;
+ }
+ }
+ if (shift) {
+ ch = unformatted.charAt(--endPos);
+ shift = endPos > startPos;
+ }
+ }
+ writer.append(unformatted.substring(startPos, endPos + 1));
+ if (unformatted.charAt(endPos) != '\n')
+ writer.append(LINE_SEPARATOR);
+ startPos = endPos + 1;
+ }
+ }
+ }
+
+}