Author: jens
Date: Wed Aug 24 08:59:21 2016
New Revision: 1757477
URL: http://svn.apache.org/viewvc?rev=1757477&view=rev
Log:
Add content generating code to InMemory sources to make test-util module
obsolete
Added:
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/ObjectGenerator.java
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/ComplexPoint.java
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/ComplexRectangle.java
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/FractalCalculator.java
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/FractalGenerator.java
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/loremipsum/
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/loremipsum/LoremIpsum.java
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/test/java/org/apache/chemistry/opencmis/inmemory/content/
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/test/java/org/apache/chemistry/opencmis/inmemory/content/loremipsum/
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/test/java/org/apache/chemistry/opencmis/inmemory/content/loremipsum/LoremIpsumTest.java
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/test/resources/HaenselUndGretel.txt
Modified:
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/pom.xml
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/server/InMemoryServiceFactoryImpl.java
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/test/java/org/apache/chemistry/opencmis/inmemory/DiscoveryServiceTest.java
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/test/java/org/apache/chemistry/opencmis/inmemory/NavigationServiceTest.java
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/test/java/org/apache/chemistry/opencmis/inmemory/ObjectServiceTest.java
chemistry/opencmis/trunk/pom.xml
Modified:
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/pom.xml
URL:
http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/pom.xml?rev=1757477&r1=1757476&r2=1757477&view=diff
==============================================================================
---
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/pom.xml
(original)
+++
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/pom.xml
Wed Aug 24 08:59:21 2016
@@ -93,11 +93,6 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
- <artifactId>chemistry-opencmis-test-util</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
<artifactId>chemistry-opencmis-server-support</artifactId>
<version>${project.version}</version>
</dependency>
Added:
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/ObjectGenerator.java
URL:
http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/ObjectGenerator.java?rev=1757477&view=auto
==============================================================================
---
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/ObjectGenerator.java
(added)
+++
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/ObjectGenerator.java
Wed Aug 24 08:59:21 2016
@@ -0,0 +1,667 @@
+/*
+ * 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.inmemory.content;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.chemistry.opencmis.commons.PropertyIds;
+import org.apache.chemistry.opencmis.commons.data.Acl;
+import org.apache.chemistry.opencmis.commons.data.ContentStream;
+import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
+import org.apache.chemistry.opencmis.commons.data.ObjectData;
+import org.apache.chemistry.opencmis.commons.data.ObjectInFolderData;
+import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList;
+import org.apache.chemistry.opencmis.commons.data.Properties;
+import org.apache.chemistry.opencmis.commons.data.PropertyData;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
+import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
+import org.apache.chemistry.opencmis.commons.enums.VersioningState;
+import org.apache.chemistry.opencmis.commons.impl.IOUtils;
+import
org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
+import org.apache.chemistry.opencmis.commons.spi.BindingsObjectFactory;
+import org.apache.chemistry.opencmis.commons.spi.NavigationService;
+import org.apache.chemistry.opencmis.commons.spi.ObjectService;
+import org.apache.chemistry.opencmis.commons.spi.RepositoryService;
+import org.apache.chemistry.opencmis.inmemory.content.fractal.FractalGenerator;
+import org.apache.chemistry.opencmis.inmemory.content.loremipsum.LoremIpsum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A simple helper class for the tests that generates a sample folder hierarchy
+ * and optionally documents in it.
+ */
+public class ObjectGenerator {
+
+ private static final int KILO = 1024;
+ private static final Logger LOG =
LoggerFactory.getLogger(ObjectGenerator.class);
+ private final BindingsObjectFactory fFactory;
+ NavigationService fNavSvc;
+ ObjectService fObjSvc;
+ RepositoryService fRepSvc;
+ private final String fRepositoryId;
+ private boolean fCleanup;
+ List<String> fTopLevelDocsCreated; // list of ids created on first level
+ List<String> fTopLevelFoldersCreated; // list of ids created on first level
+
+ /**
+ * supported kinds of content
+ *
+ */
+ public enum ContentKind {
+ STATIC_TEXT, LOREM_IPSUM_TEXT, LOREM_IPSUM_HTML, IMAGE_FRACTAL_JPEG
+ }
+
+ /**
+ * Indicates if / how many documents are created in each folder
+ */
+ private int fNoDocumentsToCreate;
+
+ /**
+ * The type id of the document id that is created.
+ */
+ private String fDocTypeId = BaseTypeId.CMIS_DOCUMENT.value();
+
+ /**
+ * The type id of the folder that is created.
+ */
+ private String fFolderTypeId = BaseTypeId.CMIS_FOLDER.value();
+
+ /**
+ * A list of property ids. For each element in this list a String property
+ * value is created for each creation of a document. All ids must be valid
+ * string property id of the type fDocTypeId
+ */
+ private List<String> fStringPropertyIdsToSetForDocument;
+
+ /**
+ * A list of property ids. For each element in this list a String property
+ * value is created for each creation of a folder. All ids must be valid
+ * string property id of the type fFolderTypeId
+ */
+ private List<String> fStringPropertyIdsToSetForFolder;
+
+ /**
+ * number of documents created in total
+ */
+ private int fDocumentsInTotalCount = 0;
+
+ /**
+ * number of folders created in total
+ */
+ private int fFoldersInTotalCount = 0;
+
+ /**
+ * size of content in KB, if 0 create documents without content
+ */
+ private int fContentSizeInK = 0;
+
+ /**
+ * Kind of content to create
+ */
+ private ContentKind fContentKind;
+
+ private static final String NAMEPROPVALPREFIXDOC = "My_Document-";
+ private static final String NAMEPROPVALPREFIXFOLDER = "My_Folder-";
+ private static final String STRINGPROPVALPREFIXDOC = "My Doc
StringProperty ";
+ private static final String STRINGPROPVALPREFIXFOLDER = "My Folder
StringProperty ";
+ private int propValCounterDocString = 0;
+ private int propValCounterFolderString = 0;
+ /**
+ * use UUIDs to generate folder and document names
+ */
+ private boolean fUseUuids;
+
+ /**
+ * generator for images
+ */
+ private FractalGenerator fractalGenerator = null;
+
+ public ObjectGenerator(BindingsObjectFactory factory, NavigationService
navSvc, ObjectService objSvc,
+ RepositoryService repSvc, String repositoryId, ContentKind
contentKind) {
+ super();
+ fFactory = factory;
+ fNavSvc = navSvc;
+ fObjSvc = objSvc;
+ fRepSvc = repSvc;
+ fRepositoryId = repositoryId;
+ // create an empty list of properties to generate by default for folder
+ // and document
+ fStringPropertyIdsToSetForDocument = new ArrayList<String>();
+ fStringPropertyIdsToSetForFolder = new ArrayList<String>();
+ fNoDocumentsToCreate = 0;
+ fUseUuids = false;
+ fCleanup = false;
+ fTopLevelDocsCreated = new ArrayList<String>();
+ fTopLevelFoldersCreated = new ArrayList<String>();
+ fContentKind = contentKind;
+ }
+
+ public void setNumberOfDocumentsToCreatePerFolder(int noDocumentsToCreate)
{
+ fNoDocumentsToCreate = noDocumentsToCreate;
+ }
+
+ public void setFolderTypeId(String folderTypeId) {
+ fFolderTypeId = folderTypeId;
+ }
+
+ public void setDocumentTypeId(String docTypeId) {
+ fDocTypeId = docTypeId;
+ }
+
+ public void setDocumentPropertiesToGenerate(List<String> propertyIds) {
+ fStringPropertyIdsToSetForDocument = propertyIds;
+ }
+
+ public void setFolderPropertiesToGenerate(List<String> propertyIds) {
+ fStringPropertyIdsToSetForFolder = propertyIds;
+ }
+
+ public void setContentSizeInKB(int sizeInK) {
+ fContentSizeInK = sizeInK;
+ }
+
+ public ContentKind getContentKind() {
+ return fContentKind;
+ }
+
+ public void setLoreIpsumGenerator(ContentKind contentKind) {
+ fContentKind = contentKind;
+ }
+
+ public void setCleanUpAfterCreate(boolean doCleanup) {
+ fCleanup = doCleanup;
+ }
+
+ public void createFolderHierachy(int levels, int childrenPerLevel, String
rootFolderId) {
+ resetCounters();
+ fTopLevelFoldersCreated.clear();
+ fTopLevelDocsCreated.clear();
+ createFolderHierachy(rootFolderId, 0, levels, childrenPerLevel);
+ if (fCleanup) {
+ deleteTree();
+ }
+ }
+
+ public void setUseUuidsForNames(boolean useUuids) {
+ /**
+ * use UUIDs to generate folder and document names
+ */
+ fUseUuids = useUuids;
+ }
+
+ /**
+ * retrieve the index-th folder from given level of the hierarchy starting
+ * at rootId
+ *
+ * @param rootId
+ * @param level
+ * @param index
+ * @return
+ */
+ public String getFolderId(String rootId, int level, int index) {
+ String objectId = rootId;
+ final String requiredProperties = PropertyIds.OBJECT_ID + "," +
PropertyIds.OBJECT_TYPE_ID + ","
+ + PropertyIds.BASE_TYPE_ID;
+ // Note: This works because first folders are created then documents
+ for (int i = 0; i < level; i++) {
+ ObjectInFolderList result = fNavSvc.getChildren(fRepositoryId,
objectId, requiredProperties,
+ PropertyIds.OBJECT_TYPE_ID, false,
IncludeRelationships.NONE, null, true, BigInteger.valueOf(-1),
+ BigInteger.valueOf(-1), null);
+ List<ObjectInFolderData> children = result.getObjects();
+ ObjectData child = children.get(index).getObject();
+ objectId = (String)
child.getProperties().getProperties().get(PropertyIds.OBJECT_ID).getFirstValue();
+ }
+ return objectId;
+ }
+
+ /**
+ * retrieve the index-th document from given folder
+ *
+ * @param folderId
+ * folder to retrieve document from
+ * @param index
+ * index of document to retrieve from this folder
+ * @return
+ */
+ public String getDocumentId(String folderId, int index) {
+ String docId = null;
+ final String requiredProperties = PropertyIds.OBJECT_ID + "," +
PropertyIds.OBJECT_TYPE_ID + ","
+ + PropertyIds.BASE_TYPE_ID;
+ ObjectInFolderList result = fNavSvc.getChildren(fRepositoryId,
folderId, requiredProperties,
+ PropertyIds.OBJECT_TYPE_ID, false, IncludeRelationships.NONE,
null, true, BigInteger.valueOf(-1),
+ BigInteger.valueOf(-1), null);
+ List<ObjectInFolderData> children = result.getObjects();
+ int numDocsFound = 0;
+ for (int i = 0; i < children.size(); i++) {
+ ObjectData child = children.get(i).getObject();
+ docId = (String)
child.getProperties().getProperties().get(PropertyIds.OBJECT_ID).getFirstValue();
+ if (child.getBaseTypeId().equals(BaseTypeId.CMIS_DOCUMENT)) {
+ if (numDocsFound == index) {
+ return docId;
+ } else {
+ numDocsFound++;
+ }
+ }
+ }
+ return docId;
+ }
+
+ /**
+ * return the total number of documents created
+ *
+ * @return
+ */
+ public int getDocumentsInTotal() {
+ return fDocumentsInTotalCount;
+ }
+
+ /**
+ * return the total number of folders created
+ *
+ * @return
+ */
+ public int getFoldersInTotal() {
+ return fFoldersInTotalCount;
+ }
+
+ /**
+ * return the total number of objects created
+ */
+ public int getObjectsInTotal() {
+ return fDocumentsInTotalCount + fFoldersInTotalCount;
+ }
+
+ public String createSingleDocument(String folderId) {
+ String objectId = createDocument(folderId, 0, 0);
+ if (fCleanup) {
+ deleteObject(objectId);
+ }
+ return objectId;
+ }
+
+ public String[] createDocuments(String folderId, int count) {
+ String[] result;
+
+ for (int i = 0; i < count; i++) {
+ String id = createDocument(folderId, 0, 0);
+ fTopLevelDocsCreated.add(id);
+ }
+ if (fCleanup) {
+ deleteTree();
+ result = null;
+ } else {
+ result = new String[count];
+ for (int i = 0; i < fTopLevelDocsCreated.size(); i++) {
+ result[i] = fTopLevelDocsCreated.get(i);
+ }
+ }
+ return result;
+ }
+
+ public String[] createFolders(String folderId, int count) {
+ String[] result;
+
+ for (int i = 0; i < count; i++) {
+ createFolder(folderId);
+ }
+ if (fCleanup) {
+ deleteTree();
+ result = null;
+ } else {
+ result = new String[count];
+ for (int i = 0; i < fTopLevelFoldersCreated.size(); i++) {
+ result[i] = fTopLevelFoldersCreated.get(i);
+ }
+ }
+ return result;
+ }
+
+ public void resetCounters() {
+ fDocumentsInTotalCount = 0;
+ fFoldersInTotalCount = 0;
+ }
+
+ private void createFolderHierachy(String parentId, int level, int levels,
int childrenPerLevel) {
+ String id = null;
+
+ if (level >= levels) {
+ return;
+ }
+
+ LOG.debug(" create folder for parent id: " + parentId + ", in level "
+ level + ", max levels " + levels);
+
+ for (int i = 0; i < childrenPerLevel; i++) {
+ Properties props = createFolderProperties(i, level);
+ id = fObjSvc.createFolder(fRepositoryId, props, parentId, null,
null, null, null);
+ if (level == 0) {
+ fTopLevelFoldersCreated.add(id);
+ }
+
+ if (id != null) {
+ ++fFoldersInTotalCount;
+ createFolderHierachy(id, level + 1, levels, childrenPerLevel);
+ }
+ }
+ for (int j = 0; j < fNoDocumentsToCreate; j++) {
+ id = createDocument(parentId, j, level);
+ if (level == 0) {
+ fTopLevelDocsCreated.add(id);
+ }
+ }
+ }
+
+ private String createFolder(String parentId) {
+ Properties props = createFolderProperties(0, 0);
+ String id = null;
+ id = fObjSvc.createFolder(fRepositoryId, props, parentId, null, null,
null, null);
+ fTopLevelFoldersCreated.add(id);
+ return id;
+ }
+
+ private String createDocument(String folderId, int no, int level) {
+ ContentStream contentStream = null;
+ VersioningState versioningState = VersioningState.NONE;
+ List<String> policies = null;
+ Acl addACEs = null;
+ Acl removeACEs = null;
+ ExtensionsData extension = null;
+
+ LOG.debug("create document in folder " + folderId);
+ Properties props = createDocumentProperties(no, level);
+ String id = null;
+
+ if (fContentSizeInK > 0) {
+ switch (fContentKind) {
+ case STATIC_TEXT:
+ contentStream = createContentStaticText();
+ break;
+ case LOREM_IPSUM_TEXT:
+ contentStream = createContentLoremIpsumText();
+ break;
+ case LOREM_IPSUM_HTML:
+ contentStream = createContentLoremIpsumHtml();
+ break;
+ case IMAGE_FRACTAL_JPEG:
+ contentStream = createContentFractalimageJpeg();
+ break;
+ }
+ }
+
+ id = fObjSvc.createDocument(fRepositoryId, props, folderId,
contentStream, versioningState, policies,
+ addACEs, removeACEs, extension);
+
+ if (null == id) {
+ LOG.error("createDocument failed.");
+ }
+ ++fDocumentsInTotalCount;
+ return id;
+ }
+
+ private void deleteTree() {
+
+ // delete all documents from first level
+ for (String id : fTopLevelDocsCreated) {
+ deleteObject(id);
+ }
+
+ // delete recursively all folders from first level
+ for (String id : fTopLevelFoldersCreated) {
+ fObjSvc.deleteTree(fRepositoryId, id, true,
UnfileObject.DELETE, true, null);
+ }
+ }
+
+ private void deleteObject(String objectId) {
+ fObjSvc.deleteObject(fRepositoryId, objectId, true, null);
+ }
+
+ public ContentStream createContentLoremIpsumHtml() {
+ ContentStreamImpl content = new ContentStreamImpl();
+ content.setFileName("data.html");
+ content.setMimeType("text/html");
+ int len = fContentSizeInK * KILO; // size of document in K
+
+ LoremIpsum ipsum = new LoremIpsum();
+ String text = ipsum.generateParagraphsFullHtml(len, true);
+
+ content.setStream(new ByteArrayInputStream(IOUtils.toUTF8Bytes(text)));
+ return content;
+ }
+
+ public ContentStream createContentLoremIpsumText() {
+ ContentStreamImpl content = new ContentStreamImpl();
+ content.setFileName("data.txt");
+ content.setMimeType("text/plain");
+ int len = fContentSizeInK * 1024; // size of document in K
+
+ LoremIpsum ipsum = new LoremIpsum();
+ String text = ipsum.generateParagraphsPlainText(len, 80, true);
+ content.setStream(new ByteArrayInputStream(IOUtils.toUTF8Bytes(text)));
+ return content;
+ }
+
+ public ContentStream createContentStaticText() {
+ ContentStreamImpl content = new ContentStreamImpl();
+ content.setFileName("data.txt");
+ content.setMimeType("text/plain");
+ int len = fContentSizeInK * 1024; // size of document in K
+ byte[] b = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x0c, 0x0a,
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
0x6b, 0x6c, 0x6d, 0x6e, 0x0c, 0x0a }; // 32
+ // Bytes
+ ByteArrayOutputStream ba = new ByteArrayOutputStream(len);
+ try {
+ for (int j = 0; j < fContentSizeInK; j++) {
+ // write 1K of data
+ for (int i = 0; i < 32; i++) {
+ ba.write(b);
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to fill content stream with
data", e);
+ }
+ content.setStream(new ByteArrayInputStream(ba.toByteArray()));
+ return content;
+ }
+
+ public ContentStream createContentFractalimageJpeg() {
+ if (null == fractalGenerator) {
+ fractalGenerator = new FractalGenerator();
+ }
+
+ ContentStreamImpl content = null;
+
+ try {
+ ByteArrayOutputStream bos = fractalGenerator.generateFractal();
+ content = new ContentStreamImpl();
+ content.setFileName("image.jpg");
+ content.setMimeType("image/jpeg");
+ content.setStream(new ByteArrayInputStream(bos.toByteArray()));
+ bos.close();
+ } catch (IOException e) {
+ System.err.println("Error when generating fractal image: " + e);
+ e.printStackTrace();
+ }
+
+ return content;
+ }
+
+ private Properties createFolderProperties(int no, int level) {
+ List<PropertyData<?>> properties = new ArrayList<PropertyData<?>>();
+ properties.add(fFactory.createPropertyStringData(PropertyIds.NAME,
generateFolderNameValue(no, level)));
+
properties.add(fFactory.createPropertyIdData(PropertyIds.OBJECT_TYPE_ID,
fFolderTypeId));
+ // Generate some property values for custom attributes
+ for (String stringPropId : fStringPropertyIdsToSetForFolder) {
+ properties.add(fFactory.createPropertyStringData(stringPropId,
generateStringPropValueFolder()));
+ }
+ Properties props = fFactory.createPropertiesData(properties);
+ return props;
+ }
+
+ private Properties createDocumentProperties(int no, int level) {
+ List<PropertyData<?>> properties = new ArrayList<PropertyData<?>>();
+ properties.add(fFactory.createPropertyStringData(PropertyIds.NAME,
generateDocNameValue(no, level)));
+
properties.add(fFactory.createPropertyIdData(PropertyIds.OBJECT_TYPE_ID,
fDocTypeId));
+ // Generate some property values for custom attributes
+ for (String stringPropId : fStringPropertyIdsToSetForDocument) {
+ properties.add(fFactory.createPropertyStringData(stringPropId,
generateStringPropValueDoc()));
+ }
+ Properties props = fFactory.createPropertiesData(properties);
+ return props;
+ }
+
+ private synchronized int incrementPropCounterDocStringProp() {
+ return propValCounterDocString++;
+ }
+
+ private synchronized int incrementPropCounterFolderStringProp() {
+ return propValCounterFolderString++;
+ }
+
+ private String generateDocNameValue(int no, int level) {
+ if (fUseUuids) {
+ return UUID.randomUUID().toString();
+ } else {
+ return NAMEPROPVALPREFIXDOC + level + "-" + no;
+ }
+ }
+
+ private String generateFolderNameValue(int no, int level) {
+ if (fUseUuids) {
+ return UUID.randomUUID().toString();
+ } else {
+ return NAMEPROPVALPREFIXFOLDER + level + "-" + no;
+ }
+ }
+
+ private String generateStringPropValueDoc() {
+ return STRINGPROPVALPREFIXDOC + incrementPropCounterDocStringProp();
+ }
+
+ private String generateStringPropValueFolder() {
+ return STRINGPROPVALPREFIXFOLDER +
incrementPropCounterFolderStringProp();
+ }
+
+ public void dumpFolder(String folderId, String propertyFilter) {
+ LOG.debug("starting dumpFolder() id " + folderId + " ...");
+ boolean allRequiredPropertiesArePresent = propertyFilter != null &&
propertyFilter.equals("*"); // can
+ // be
+ // optimized
+ final String requiredProperties = allRequiredPropertiesArePresent ?
propertyFilter : PropertyIds.OBJECT_ID
+ + "," + PropertyIds.NAME + "," + PropertyIds.OBJECT_TYPE_ID +
"," + PropertyIds.BASE_TYPE_ID;
+ // if all required properties are contained in the filter use we use
the
+ // filter otherwise
+ // we use our own set and get those from the filter later in an extra
+ // call
+ String propertyFilterIntern = allRequiredPropertiesArePresent ?
propertyFilter : requiredProperties;
+ dumpFolder(folderId, propertyFilterIntern, 0);
+ }
+
+ private void dumpFolder(String folderId, String propertyFilter, int depth)
{
+ boolean allRequiredPropertiesArePresent = propertyFilter.equals("*");
// can
+ // be
+ // optimized
+ StringBuilder prefix = new StringBuilder();
+ for (int i = 0; i < depth; i++) {
+ prefix.append(" ");
+ }
+
+ ObjectInFolderList result = fNavSvc.getChildren(fRepositoryId,
folderId, propertyFilter, null, false,
+ IncludeRelationships.NONE, null, true, BigInteger.valueOf(-1),
BigInteger.valueOf(-1), null);
+ List<ObjectInFolderData> folders = result.getObjects();
+ if (null != folders) {
+ LOG.debug(prefix + "found " + folders.size() + " children in
folder " + folderId);
+ int no = 0;
+ for (ObjectInFolderData folder : folders) {
+ LOG.debug(prefix.toString() + ++no + ": found object with id:
" + folder.getObject().getId()
+ + " and path segment: " + folder.getPathSegment());
+ dumpObjectProperties(folder.getObject(), depth,
propertyFilter, !allRequiredPropertiesArePresent);
+ String objectTypeBaseId =
folder.getObject().getBaseTypeId().value();
+ if (objectTypeBaseId.equals(BaseTypeId.CMIS_FOLDER.value())) {
+ dumpFolder(folder.getObject().getId(), propertyFilter,
depth + 1);
+ } else if
(objectTypeBaseId.equals(BaseTypeId.CMIS_DOCUMENT.value())) {
+ dumpObjectProperties(folder.getObject(), depth + 1,
propertyFilter,
+ !allRequiredPropertiesArePresent);
+ }
+ }
+ }
+ LOG.debug(""); // add empty line
+ }
+
+ private void dumpObjectProperties(ObjectData object, int depth, String
propertyFilter, boolean mustFetchProperties) {
+ final SimpleDateFormat df = new
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
+ StringBuilder prefix = new StringBuilder();
+ for (int i = 0; i < depth; i++) {
+ prefix.append(" ");
+ }
+
+ LOG.debug(prefix + "found object id " + object.getId());
+ Map<String, PropertyData<?>> propMap;
+ if (mustFetchProperties) {
+ String objId = (String)
object.getProperties().getProperties().get(PropertyIds.OBJECT_ID).getFirstValue();
+ Properties props = fObjSvc.getProperties(fRepositoryId, objId,
propertyFilter, null);
+ propMap = props.getProperties();
+ } else {
+ propMap = object.getProperties().getProperties();
+ }
+ StringBuilder valueStr = new StringBuilder("[");
+ for (Map.Entry<String, PropertyData<?>> entry : propMap.entrySet()) {
+ if (entry.getValue().getValues().size() > 1) {
+ if (entry.getValue().getFirstValue() instanceof
GregorianCalendar) {
+ for (Object obj : entry.getValue().getValues()) {
+ GregorianCalendar cal = (GregorianCalendar) obj;
+ valueStr.append(df.format(cal.getTime())).append(", ");
+ }
+ valueStr.append("]");
+ } else {
+ valueStr = new
StringBuilder(entry.getValue().getValues().toString());
+ }
+ } else {
+ Object value = entry.getValue().getFirstValue();
+ if (null != value) {
+ valueStr = new StringBuilder(value.toString());
+ if (value instanceof GregorianCalendar) {
+ valueStr = new
StringBuilder(df.format(((GregorianCalendar) entry.getValue().getFirstValue())
+ .getTime()));
+ }
+ }
+ }
+ LOG.debug(prefix + entry.getKey() + ": " + valueStr);
+ }
+ LOG.debug(""); // add empty line
+ }
+
+ public void createTypes(TypeDefinitionList typeDefList) {
+ for (TypeDefinition td : typeDefList.getList()) {
+ // TODO: enable this if available!
+ // fRepSvc.createTypeDefinition(fRepositoryId, td);
+ }
+ }
+}
Added:
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/ComplexPoint.java
URL:
http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/ComplexPoint.java?rev=1757477&view=auto
==============================================================================
---
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/ComplexPoint.java
(added)
+++
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/ComplexPoint.java
Wed Aug 24 08:59:21 2016
@@ -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.inmemory.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-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/ComplexRectangle.java
URL:
http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/ComplexRectangle.java?rev=1757477&view=auto
==============================================================================
---
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/ComplexRectangle.java
(added)
+++
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/ComplexRectangle.java
Wed Aug 24 08:59:21 2016
@@ -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.inmemory.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-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/FractalCalculator.java
URL:
http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/FractalCalculator.java?rev=1757477&view=auto
==============================================================================
---
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/FractalCalculator.java
(added)
+++
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/FractalCalculator.java
Wed Aug 24 08:59:21 2016
@@ -0,0 +1,165 @@
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.inmemory.content.fractal;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.util.Arrays;
+
+final class FractalCalculator {
+ private int[] colorMap;
+ protected int[][] noIterations;
+ 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 = Arrays.copyOf(colMap, colMap.length);
+ 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 int[][] calcFractal() {
+ noIterations = new int[ imageWidth ][ imageHeight ];
+
+ // For each pixel...
+ for (int x = 0; x < imageWidth; x++) {
+ for (int y = 0; y < imageHeight; y++) {
+ double zR = rRangeMin + x * delta;
+ double zI = iRangeMin + (imageHeight - y) * delta;
+
+ // Is the point inside the set?
+ if (useJulia) {
+ noIterations[x][y] = testPointJuliaSet(zR, zI,
maxIterations);
+ } else {
+ noIterations[x][y] = testPointMandelbrot(zR, zI,
maxIterations);
+ }
+ }
+ }
+ return noIterations;
+ }
+
+ public BufferedImage mapItersToColors(int[][] iterations) {
+
+ // 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(iterations[x][y]);
+ image.setRGB(x, y, color);
+ }
+ }
+ return image;
+ }
+
+ protected int getColor(int numIterations) {
+ int c = Color.black.getRGB();
+
+ 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-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/FractalGenerator.java
URL:
http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/FractalGenerator.java?rev=1757477&view=auto
==============================================================================
---
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/FractalGenerator.java
(added)
+++
chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-inmemory/src/main/java/org/apache/chemistry/opencmis/inmemory/content/fractal/FractalGenerator.java
Wed Aug 24 08:59:21 2016
@@ -0,0 +1,484 @@
+////////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 [email protected], but address is invalid (2012-02-07)
+ */
+
+package org.apache.chemistry.opencmis.inmemory.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.security.SecureRandom;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FractalGenerator {
+ private static final Logger LOG =
LoggerFactory.getLogger(FractalGenerator.class);
+
+ private static final int ZOOM_STEPS_PER_BATCH = 10;
+ private static final int DEFAULT_MAX_ITERATIONS = 33;
+ private static final ComplexRectangle INITIAL_RECT = new
ComplexRectangle(-2.1, 1.1, -1.3, 1.3);
+ private static final ComplexRectangle INITIAL_JULIA_RECT = new
ComplexRectangle(-2.0, 2.0, -2.0, 2.0);
+ private static final int INITIAL_ITERATIONS = 33;
+
+ // Color:
+ private Map<String, int[]> colorTable;
+ private static final String COLORS_BLACK_AND_WHITE = "black & white";
+ private static final String COLORS_BLUE_ICE = "blue ice";
+ private static final String COLORS_FUNKY = "funky";
+ private static final String COLORS_PASTEL = "pastel";
+ private static final String COLORS_PSYCHEDELIC = "psychedelic";
+ private static final String COLORS_PURPLE_HAZE = "purple haze";
+ private static final String COLORS_RADICAL = "radical";
+ private static final String COLORS_RAINBOW = "rainbow";
+ private static final String COLORS_RAINBOWS = "rainbows";
+ private static final String COLORS_SCINTILLATION = "scintillation";
+ private static final String COLORS_WARPED = "warped";
+ private static final String COLORS_WILD = "wild";
+ private static final String COLORS_ZEBRA = "zebra";
+ private static 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 static final int IMAGE_HEIGHT = 512; // default
+ private static final int IMAGE_WIDTH = 512; // default
+ private static final int NUM_COLORS = 512; // colors per colormap
+ private FractalCalculator calculator;
+ private int previousIterations = 1;
+ private int maxIterations;
+ private String color;
+ private int newRowTile, newColTile;
+ private int parts = 16;
+ private int stepInBatch = 0;
+ private ComplexRectangle rect;
+ private 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;
+
+ SecureRandom ran = new SecureRandom();
+ color = colorSchemes[ran.nextInt(colorSchemes.length)];
+ parts = ran.nextInt(13) + 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);
+ randomizeRect(rect);
+ 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, IMAGE_WIDTH,
IMAGE_HEIGHT, getCurrentColorMap(),
+ juliaPoint);
+ int[][] iterations = calculator.calcFractal();
+ BufferedImage image = calculator.mapItersToColors(iterations);
+ findNewRect(image, iterations);
+
+ // 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();
+
+ 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 ((IMAGE_WIDTH > 0) && (IMAGE_HEIGHT > 0)) {
+ imageWHRatio = ((double) IMAGE_WIDTH / (double) IMAGE_HEIGHT);
+ }
+
+ if ((complexWidth > 0) && (complexHeight > 0)) {
+ complexWHRatio = complexWidth / complexHeight;
+ } else {
+ return;
+ }
+
+ if (Double.compare(imageWHRatio, complexWHRatio) == 0) {
+ 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() / ((IMAGE_WIDTH) * 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[NUM_COLORS];
+ for (int colorNum = NUM_COLORS - 1; colorNum >= 0; colorNum--) {
+ colorMap[colorNum] = Color.white.getRGB();
+ }
+ colorTable.put(COLORS_BLACK_AND_WHITE, colorMap);
+
+ // COLORS_BLUE_ICE:
+ blue = 255;
+ colorMap = new int[NUM_COLORS];
+ for (int colorNum = NUM_COLORS - 1; colorNum >= 0; colorNum--) {
+ red = (int) ((255 * (float) colorNum / NUM_COLORS)) % 255;
+ green = (int) ((255 * (float) colorNum / NUM_COLORS)) % 255;
+ colorMap[colorNum] = new Color(red, green, blue).getRGB();
+ }
+ colorTable.put(COLORS_BLUE_ICE, colorMap);
+
+ // COLORS_FUNKY:
+ colorMap = new int[NUM_COLORS];
+ for (int colorNum = NUM_COLORS - 1; colorNum >= 0; colorNum--) {
+ red = (int) ((1024 * (float) colorNum / NUM_COLORS)) % 255;
+ green = (int) ((512 * (float) colorNum / NUM_COLORS)) % 255;
+ blue = (int) ((256 * (float) colorNum / NUM_COLORS)) % 255;
+ colorMap[NUM_COLORS - colorNum - 1] = new Color(red, green,
blue).getRGB();
+ }
+ colorTable.put(COLORS_FUNKY, colorMap);
+
+ // COLORS_PASTEL
+ brightness = (float) 1.0;
+ colorMap = new int[NUM_COLORS];
+ for (int colorNum = 0; colorNum < NUM_COLORS; colorNum++) {
+ hue = ((float) (colorNum * 4) / (float) NUM_COLORS) % NUM_COLORS;
+ saturation = ((float) (colorNum * 2) / (float) NUM_COLORS) %
NUM_COLORS;
+ colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
+ }
+ colorTable.put(COLORS_PASTEL, colorMap);
+
+ // COLORS_PSYCHEDELIC:
+ saturation = (float) 1.0;
+ colorMap = new int[NUM_COLORS];
+ for (int colorNum = 0; colorNum < NUM_COLORS; colorNum++) {
+ hue = ((float) (colorNum * 5) / (float) NUM_COLORS) % NUM_COLORS;
+ brightness = ((float) (colorNum * 20) / (float) NUM_COLORS) %
NUM_COLORS;
+ colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
+ }
+ colorTable.put(COLORS_PSYCHEDELIC, colorMap);
+
+ // COLORS_PURPLE_HAZE:
+ red = 255;
+ blue = 255;
+ colorMap = new int[NUM_COLORS];
+ for (int colorNum = NUM_COLORS - 1; colorNum >= 0; colorNum--) {
+ green = (int) ((255 * (float) colorNum / NUM_COLORS)) % 255;
+ colorMap[NUM_COLORS - colorNum - 1] = new Color(red, green,
blue).getRGB();
+ }
+ colorTable.put(COLORS_PURPLE_HAZE, colorMap);
+
+ // COLORS_RADICAL:
+ saturation = (float) 1.0;
+ colorMap = new int[NUM_COLORS];
+ for (int colorNum = 0; colorNum < NUM_COLORS; colorNum++) {
+ hue = ((float) (colorNum * 7) / (float) NUM_COLORS) % NUM_COLORS;
+ brightness = ((float) (colorNum * 49) / (float) NUM_COLORS) %
NUM_COLORS;
+ 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[NUM_COLORS];
+ for (int colorNum = 0; colorNum < NUM_COLORS; colorNum++) {
+ hue = (float) colorNum / (float) NUM_COLORS;
+ 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[NUM_COLORS];
+ for (int colorNum = 0; colorNum < NUM_COLORS; colorNum++) {
+ hue = ((float) (colorNum * 5) / (float) NUM_COLORS) % NUM_COLORS;
+ 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[NUM_COLORS];
+ for (int colorNum = 0; colorNum < NUM_COLORS; colorNum++) {
+ hue = ((float) (colorNum * 2) / (float) NUM_COLORS) % NUM_COLORS;
+ brightness = ((float) (colorNum * 5) / (float) NUM_COLORS) %
NUM_COLORS;
+ colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
+ }
+ colorTable.put(COLORS_SCINTILLATION, colorMap);
+
+ // COLORS_WARPED:
+ colorMap = new int[NUM_COLORS];
+ for (int colorNum = NUM_COLORS - 1; colorNum >= 0; colorNum--) {
+ red = (int) ((1024 * (float) colorNum / NUM_COLORS)) % 255;
+ green = (int) ((256 * (float) colorNum / NUM_COLORS)) % 255;
+ blue = (int) ((512 * (float) colorNum / NUM_COLORS)) % 255;
+ colorMap[NUM_COLORS - colorNum - 1] = new Color(red, green,
blue).getRGB();
+ }
+ colorTable.put(COLORS_WARPED, colorMap);
+
+ // COLORS_WILD:
+ colorMap = new int[NUM_COLORS];
+ for (int colorNum = 0; colorNum < NUM_COLORS; colorNum++) {
+ hue = ((float) (colorNum * 1) / (float) NUM_COLORS) % NUM_COLORS;
+ saturation = ((float) (colorNum * 2) / (float) NUM_COLORS) %
NUM_COLORS;
+ brightness = ((float) (colorNum * 4) / (float) NUM_COLORS) %
NUM_COLORS;
+ colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
+ }
+ colorTable.put(COLORS_WILD, colorMap);
+
+ // COLORS_ZEBRA:
+ colorMap = new int[NUM_COLORS];
+ for (int colorNum = 0; colorNum < NUM_COLORS; 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[][] iterations) {
+
+ 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];
+
+ 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);
+ stdDev[i * parts + j] = calcStdDev(iterations, subRect);
+ ++j;
+ }
+ ++i;
+ j = 0;
+ }
+
+ // find tile with greatest std deviation:
+ double max = 0;
+ int index = 0;
+ for (i = 0; i < noTiles; i++) {
+ if (stdDev[i] > max) {
+ index = i;
+ max = stdDev[i];
+ }
+ }
+ newRowTile = index / parts;
+ newColTile = index % parts;
+ }
+
+ private double calcStdDev(int[][] iterations, Rectangle rect) {
+
+ int sum = 0;
+ long sumSquare = 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 iters = iterations[x][y];
+ sum += iters;
+ sumSquare += iters * iters;
+ }
+ }
+ int count = rect.width * rect.height;
+ double mean = 0.0;
+
+ mean = (double) sum / count;
+ return Math.sqrt(sumSquare / (count - (mean * mean)));
+ }
+
+}