You are viewing a plain text version of this content. The canonical link for it is here.
Posted to easyant-commits@incubator.apache.org by hi...@apache.org on 2011/02/17 17:01:56 UTC
svn commit: r1071697 [42/42] - in /incubator/easyant: buildtypes/
buildtypes/trunk/ buildtypes/trunk/build-osgi-bundle-java/
buildtypes/trunk/build-osgi-bundle-java/src/
buildtypes/trunk/build-osgi-bundle-java/src/main/
buildtypes/trunk/build-osgi-bund...
Added: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/main/java/org/apache/easyant/menu/XookiMenuGenerator.java
URL: http://svn.apache.org/viewvc/incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/main/java/org/apache/easyant/menu/XookiMenuGenerator.java?rev=1071697&view=auto
==============================================================================
--- incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/main/java/org/apache/easyant/menu/XookiMenuGenerator.java (added)
+++ incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/main/java/org/apache/easyant/menu/XookiMenuGenerator.java Thu Feb 17 17:01:07 2011
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2008-2010 the EasyAnt project
+ *
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership.
+ *
+ * Licensed 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.easyant.menu;
+
+import org.apache.easyant.core.menu.MenuGenerator;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.*;
+import java.util.NoSuchElementException;
+
+/**
+ * Generates a <a href="http://xooki.sourceforge.net/">xooki</a> JSON menu file.
+ */
+public class XookiMenuGenerator implements MenuGenerator<XookiMenuGenerator> {
+
+ private boolean closed;
+
+ private String id;
+ private File path;
+ private Writer fileWriter;
+
+ private Block currentBlock;
+
+ public void startMenu(String title, String location) throws IOException {
+ if (fileWriter != null) {
+ throw new IllegalStateException("Menu has already been opened for writing at " + path.getAbsolutePath());
+ }
+ closed = false;
+
+ path = new File(location);
+ fileWriter = new BufferedWriter(new FileWriter(path));
+ currentBlock = new Block();
+
+ //start the toplevel menu object.
+ currentBlock = currentBlock.startObject();
+
+ if (title == null) {
+ id = toId(location);
+ currentBlock.appendAttribute("id", id);
+ } else {
+ id = toId(title);
+ currentBlock
+ .appendAttribute("id", id)
+ .appendAttribute("title", title);
+ }
+
+ //add all child entries to an array called "children"
+ currentBlock = currentBlock.startArray("children");
+ }
+
+ public void addSubMenu(String title, XookiMenuGenerator subMenu) throws IOException {
+ assertOpen();
+ subMenu.assertOpen();
+
+ currentBlock
+ .startObject()
+ .appendAttribute("title", title)
+ .appendAttribute("importNode", subMenu.id)
+ .appendAttribute("importRoot", computeSubMenuPath(subMenu))
+ .end();
+ }
+
+ public void addEntry(String title, String targetLink) throws IOException {
+ assertOpen();
+
+ currentBlock
+ .startObject()
+ .appendAttribute("id", targetLink)
+ .appendAttribute("title", title)
+ .end();
+ }
+
+ public void endMenu() throws IOException {
+ assertOpen();
+ try {
+ currentBlock
+ .end() //end "children" array
+ .end(); //end toplevel {} block
+ } finally {
+ try {
+ fileWriter.close();
+ } finally {
+ fileWriter = null;
+ closed = true;
+ currentBlock = null;
+ }
+ }
+ }
+
+ /**
+ * Convert the filename for the given submenu into a path relative to this menu.
+ * @throws IOException if the conversion fails for any reason
+ */
+ private String computeSubMenuPath(XookiMenuGenerator subMenu) throws IOException {
+
+ File basePath = this.path.getParentFile();
+ File subPath = subMenu.path.getParentFile();
+
+ String path;
+ try {
+ path = FileUtils.getRelativePath(basePath, subPath);
+ } catch (Exception e) {
+ //getRelativePath throws java.lang.Exception, for no clear reason, but we have to handle it.
+ IOException ioe = new IOException("Error computing relative path for submenu " + subMenu.id);
+ ioe.initCause(e);
+ throw ioe;
+ }
+
+ if (path == null)
+ throw new FileNotFoundException("Unable to compute relative path for submenu " + subMenu.id);
+
+ return path;
+ }
+
+ private void assertOpen() throws IOException {
+ if (closed) {
+ throw new IllegalStateException("The menu at " + path.getAbsolutePath() + " has already been closed");
+ }
+ if (fileWriter == null) {
+ throw new IllegalStateException("This menu has never been opened");
+ }
+ }
+
+ private String toId(String title) {
+ return title.replaceAll("\\W+", "_");
+ }
+
+ /**
+ * Represents a single block-level element in JSON, e.g. an Array or an Object. A simple API is provided
+ * to add nested blocks and attributes.
+ */
+ private class Block {
+
+ private static final String FIRST_ENTRY_SEP = "\n";
+ private static final String NEXT_ENTRY_SEP = ",\n";
+
+ private Block parent; //points up the context stack
+ private char close; //character to write when this block is ended
+ private String indent; //current indent level
+ private String entrySeparator = FIRST_ENTRY_SEP; //separator string between entries of this block
+
+ /** constructor for the root block */
+ public Block() {
+ this.parent = null;
+ this.indent = "";
+ this.entrySeparator = "";
+ }
+
+ /** constructor for a nested block */
+ private Block(Block parent, char close) {
+ this.parent = parent;
+ this.indent = parent.indent + "\t";
+ this.close = close;
+ }
+
+ /** add a JavaScript attribute to this block */
+ public Block appendAttribute(String name, String value) throws IOException {
+ nextEntry().append('\"').append(name).append("\":");
+ return appendLiteral(value);
+ }
+
+ /** begin a nested array block with the given name */
+ public Block startArray(String name) throws IOException {
+ nextEntry().append('\"').append(name).append("\": ");
+ fileWriter.append('[');
+ return new Block(this, ']');
+ }
+
+ /** begin a nested object block */
+ public Block startObject() throws IOException {
+ nextEntry().append('{');
+ return new Block(this, '}');
+ }
+
+ /** close the current block, returning a reference to the parent. */
+ public Block end() throws IOException {
+ if (parent == null) {
+ throw new NoSuchElementException("Cannot pop the root element");
+ }
+ fileWriter.append('\n');
+ return parent.endBlock(close); //return reference to parent, popping the stack
+ }
+
+ /** end a child block using the given terminator */
+ private Block endBlock(char close) throws IOException {
+ fileWriter.append(indent).append(close);
+ return this;
+ }
+
+ /**
+ * start a new entry in the current block, including a separator from
+ * any previous entries and indenting whitespace
+ */
+ private Writer nextEntry() throws IOException {
+ fileWriter.append(entrySeparator).append(indent);
+ entrySeparator = NEXT_ENTRY_SEP;
+ return fileWriter;
+ }
+
+ /** append a quoted, escaped string literal to this block */
+ private Block appendLiteral(String value) throws IOException {
+ //escape any ' or " so that they don't screw up our syntax
+ value = value.replaceAll("(['\"])", "\\\\$1");
+ //enclose the value in quotes to include any whitespace
+ fileWriter.append("\"").append(value).append("\"");
+ return this;
+ }
+
+ }
+
+}
Propchange: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/main/java/org/apache/easyant/menu/XookiMenuGenerator.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/main/java/org/apache/easyant/menu/XookiMenuGenerator.java
------------------------------------------------------------------------------
svn:keywords = Date Revision Author HeadURL Id
Propchange: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/main/java/org/apache/easyant/menu/XookiMenuGenerator.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/java/org/apache/easyant/menu/XookiMenuGeneratorTest.java
URL: http://svn.apache.org/viewvc/incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/java/org/apache/easyant/menu/XookiMenuGeneratorTest.java?rev=1071697&view=auto
==============================================================================
--- incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/java/org/apache/easyant/menu/XookiMenuGeneratorTest.java (added)
+++ incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/java/org/apache/easyant/menu/XookiMenuGeneratorTest.java Thu Feb 17 17:01:07 2011
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2008-2010 the EasyAnt project
+ *
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership.
+ *
+ * Licensed 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.easyant.menu;
+
+import static org.junit.Assert.*;
+
+import org.apache.tools.ant.util.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+
+public class XookiMenuGeneratorTest {
+
+ private File menuFile;
+ private XookiMenuGenerator generator;
+
+ @Before
+ public void setUp() throws IOException {
+ menuFile = File.createTempFile("XookiMenuGeneratorTest", ".json");
+ menuFile.deleteOnExit();
+ generator = new XookiMenuGenerator();
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ generator = null;
+ if (menuFile.exists()) {
+ assertTrue(menuFile.delete());
+ }
+ menuFile = null;
+ }
+
+ /** test behavior of calling generator methods out-of-order */
+ @Test
+ public void testMenuLifecycle() throws IOException {
+ //should not be able to append an unopened menu.
+ try {
+ generator.addEntry("should", "fail");
+ fail("should not be able to add an entry to a menu that has not been started");
+ } catch (IllegalStateException expected) {}
+
+ //should not be able to append an unopened menu.
+ XookiMenuGenerator subMenu = new XookiMenuGenerator();
+ try {
+ generator.addSubMenu("unopened", subMenu);
+ fail("should not be able to add a submenu to a menu that has not been started");
+ } catch (IllegalStateException expected) {}
+
+ //should not be able to end an unopened menu
+ try {
+ generator.endMenu();
+ fail("should not be able to end a menu that has not been started");
+ } catch (IllegalStateException expected) {}
+
+ //start the menu.
+ generator.startMenu("Test", menuFile.getAbsolutePath());
+
+ //should not be able to add an unopened submenu
+ try {
+ generator.addSubMenu("unopened", subMenu);
+ fail("should not be able to add a submenu that is not yet opened");
+ } catch (IllegalStateException expected) {}
+
+ //should not be able to start a menu twice
+ File dupFile = new File(menuFile.getParentFile(), menuFile.getPath() + ".dup");
+ try {
+ generator.startMenu("Test", dupFile.getAbsolutePath());
+ fail("should not be able to start a menu twice");
+ } catch (IllegalStateException expected) {}
+ assertFalse("duplicate menu file should not have been created", dupFile.exists());
+
+ //add an entry, close the menu
+ generator.addEntry("lonely", "link");
+ generator.endMenu();
+
+ try {
+ generator.endMenu();
+ fail("should not be able to end a menu twice");
+ } catch (IllegalStateException expected) {}
+
+ //verify that the menu has the correct content.
+ assertEquals("menu contains only data delivered in correct order",
+ slurpMenu("testMenuLifecycle.json"), slurpMenu());
+ }
+
+ @Test
+ public void testEmptyMenu() throws IOException {
+ generator.startMenu("empty", menuFile.getAbsolutePath());
+ generator.endMenu();
+ assertEquals("empty menu is well-formed",
+ slurpMenu("testEmptyMenu.json"), slurpMenu());
+ }
+
+ @Test
+ public void testMultipleEntries() throws IOException {
+ generator.startMenu("three", menuFile.getAbsolutePath());
+ //also test escaping of title characters.
+ generator.addEntry("one", "path/to/one");
+ generator.addEntry("'two'", "path/to/two");
+ generator.addEntry("item \"three\"", "path/to/three");
+
+ generator.endMenu();
+ assertEquals("complex menu is well-formed",
+ slurpMenu("testMultipleEntries.json"), slurpMenu());
+ }
+
+ @Test
+ public void testSubMenu() throws IOException {
+ generator.startMenu("parent", menuFile.getAbsolutePath());
+ generator.addEntry("one", "path/to/one");
+
+ File subDir = new File(System.getProperty("java.io.tmpdir"), "XookiMenuGeneratorTestSub");
+ subDir.mkdir();
+ File subfile = File.createTempFile("XookiMenuGeneratorTest-sub", ".json", subDir);
+
+ try {
+ XookiMenuGenerator subMenu = new XookiMenuGenerator();
+ subMenu.startMenu("child", subfile.getAbsolutePath());
+
+ generator.addSubMenu("Child Menu", subMenu);
+ generator.addEntry("item \"three\"", "path/to/three");
+
+ generator.endMenu();
+ assertEquals("menu with submenu reference is well-formed",
+ slurpMenu("testSubMenu.json"), slurpMenu());
+
+ subMenu.endMenu();
+ } finally {
+ subfile.delete();
+ subDir.delete();
+ }
+ }
+
+ /** read the content of the current test menu */
+ private String slurpMenu() throws IOException {
+ FileReader reader = new FileReader(menuFile);
+ try {
+ return FileUtils.readFully(reader);
+ } finally {
+ reader.close();
+ }
+ }
+
+ /** read the given menu resource to verify test results */
+ private String slurpMenu(String resource) throws IOException {
+ URL url = getClass().getResource(resource);
+ assertNotNull("found classpath resource " + resource, url);
+ InputStreamReader reader = new InputStreamReader(url.openStream());
+ try {
+ return FileUtils.readFully(reader);
+ } finally {
+ reader.close();
+ }
+ }
+}
Propchange: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/java/org/apache/easyant/menu/XookiMenuGeneratorTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/java/org/apache/easyant/menu/XookiMenuGeneratorTest.java
------------------------------------------------------------------------------
svn:keywords = Date Revision Author HeadURL Id
Propchange: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/java/org/apache/easyant/menu/XookiMenuGeneratorTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testEmptyMenu.json
URL: http://svn.apache.org/viewvc/incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testEmptyMenu.json?rev=1071697&view=auto
==============================================================================
--- incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testEmptyMenu.json (added)
+++ incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testEmptyMenu.json Thu Feb 17 17:01:07 2011
@@ -0,0 +1,6 @@
+{
+ "id":"empty",
+ "title":"empty",
+ "children": [
+ ]
+}
\ No newline at end of file
Added: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testMenuLifecycle.json
URL: http://svn.apache.org/viewvc/incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testMenuLifecycle.json?rev=1071697&view=auto
==============================================================================
--- incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testMenuLifecycle.json (added)
+++ incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testMenuLifecycle.json Thu Feb 17 17:01:07 2011
@@ -0,0 +1,10 @@
+{
+ "id":"Test",
+ "title":"Test",
+ "children": [
+ {
+ "id":"link",
+ "title":"lonely"
+ }
+ ]
+}
\ No newline at end of file
Added: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testMultipleEntries.json
URL: http://svn.apache.org/viewvc/incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testMultipleEntries.json?rev=1071697&view=auto
==============================================================================
--- incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testMultipleEntries.json (added)
+++ incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testMultipleEntries.json Thu Feb 17 17:01:07 2011
@@ -0,0 +1,18 @@
+{
+ "id":"three",
+ "title":"three",
+ "children": [
+ {
+ "id":"path/to/one",
+ "title":"one"
+ },
+ {
+ "id":"path/to/two",
+ "title":"\'two\'"
+ },
+ {
+ "id":"path/to/three",
+ "title":"item \"three\""
+ }
+ ]
+}
\ No newline at end of file
Added: incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testSubMenu.json
URL: http://svn.apache.org/viewvc/incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testSubMenu.json?rev=1071697&view=auto
==============================================================================
--- incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testSubMenu.json (added)
+++ incubator/easyant/tasks/trunk/xooki-menu-generator/trunk/src/test/resources/org/apache/easyant/menu/testSubMenu.json Thu Feb 17 17:01:07 2011
@@ -0,0 +1,19 @@
+{
+ "id":"parent",
+ "title":"parent",
+ "children": [
+ {
+ "id":"path/to/one",
+ "title":"one"
+ },
+ {
+ "title":"Child Menu",
+ "importNode":"child",
+ "importRoot":"XookiMenuGeneratorTestSub"
+ },
+ {
+ "id":"path/to/three",
+ "title":"item \"three\""
+ }
+ ]
+}
\ No newline at end of file