http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/groovyShell/ArithmeticShellTest.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovyShell/ArithmeticShellTest.groovy b/src/main/groovy/groovyShell/ArithmeticShellTest.groovy new file mode 100644 index 0000000..2c9e982 --- /dev/null +++ b/src/main/groovy/groovyShell/ArithmeticShellTest.groovy @@ -0,0 +1,108 @@ +/* + * 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. + */ +/** +* Unit test for ArithmeticShell. +* Requires JUnit to be in path, just like any other GroovyTestCase. +* +* @author Hamlet D'Arcy +*/ +class ArithmeticShellTest extends GroovyTestCase { + + private ArithmeticShell shell + + void setUp() { + shell = new ArithmeticShell() + } + + Number evaluate(String text) { + shell.evaluate(text) + } + + void testEvaluate_SuccessfulPaths() { + assert 2.9073548971824276E135 == evaluate("((6L / 2f) - 1) ** 4.5e2") + assert -6.816387600233341 == evaluate("10 * Math.sin(15/-20)") + assert 1.0 == evaluate("Math.cos(2*Math.PI)") + assert 74.17310622494026 == shell.evaluate("80*Math.E**(-(+(11++/40)**2))") + assert 2147483646 == evaluate("Integer.MAX_VALUE - ++2%2") + assert 6 == evaluate("++(5)") + assert 0 == evaluate("5 < 4 ? 1 : 0") + assert 0 == evaluate("5 != 4 ? 0 : 1") + assert 0 == evaluate("5 < 4 ?: 0 ") + } + + void testEvaluate_StaticImportOfMath() { + assert 6.283185307179586 == evaluate("2*PI") + assert 0.5403023058681398 == evaluate("cos(1)") + assert 1.0 == evaluate("cos(2*PI)") + } + + void testEvaluate_Failures() { + shouldFail(SecurityException) { + evaluate("Double.valueOf(\"5\")") + } + + shouldFail(SecurityException) { + evaluate("import java.text.DateFormat; 5") + } + + shouldFail(SecurityException) { + evaluate("import static java.lang.System.*; 6 * out") + } + + shouldFail(SecurityException) { + evaluate("def x = 5+3;x.toString()") + } + + shouldFail(SecurityException) { + evaluate("new File();Double.valueOf('5')") + } + + shouldFail(SecurityException) { + // without statement whitelist set, this causes the arithmetic shell to + // enter an infinite loop + evaluate("while (1);") + } + + shouldFail(SecurityException) { + // without statement whitelist set, security exception is still thrown as it should, + // but with the error message that closures are not allowed, which may be confusing + evaluate("for (;;);") + } + + shouldFail(SecurityException) { + // without statement whitelist set, no exception is thrown + evaluate("if (1) 12 else 15") + } + + shouldFail(SecurityException) { + // without expression whitelist set, no exception is thrown + evaluate("[1,2]; 1") + } + + shouldFail(SecurityException) { + // without expression whitelist set, no exception is thrown + evaluate("[1:2]; 1") + } + + shouldFail(SecurityException) { + // without expression whitelist set, no exception is thrown + evaluate("new Object(); 1") + } + } +}
http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/groovyShell/BlacklistingShell.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovyShell/BlacklistingShell.groovy b/src/main/groovy/groovyShell/BlacklistingShell.groovy new file mode 100644 index 0000000..becff49 --- /dev/null +++ b/src/main/groovy/groovyShell/BlacklistingShell.groovy @@ -0,0 +1,96 @@ +/* + * 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. + */ +import org.codehaus.groovy.control.CompilerConfiguration +import org.codehaus.groovy.control.MultipleCompilationErrorsException +import org.codehaus.groovy.control.customizers.SecureASTCustomizer +import org.codehaus.groovy.control.messages.ExceptionMessage +import org.codehaus.groovy.ast.expr.MethodPointerExpression + + +/** + * The blacklisting shell is similar to a GroovyShell in that it can evaluate text as + * code and return a result. It is intended as an example of using blacklisting to prevent + * running methods on a class - in this case, java.lang.System. Please note that in creating + * any secure environment, there is no substitution for using a SecurityManager. + * + * Amoung the many different calls this class prevents are: + * System.exit(0) + * Eval.me("System.exit(0)") + * evaluate("System.exit(0)") + * (new GroovyShell()).evaluate("System.exit(0)") + * Class.forName("java.lang.System").exit(0) + * System.&exit.call(0) + * System.getMetaClass().invokeMethod("exit",0) + * def s = System; s.exit(0) + * Script t = this; t.evaluate("System.exit(0)") + * + * The restrictions required, however, also prevent the following code from working: + * println "test" + * def s = "test" ; s.count("t") + * + * @author Jim Driscoll ([email protected]) + */ +class BlacklistingShell { + + /** + * Compiles the text into a Groovy object and then executes it, returning the result. + * Prevents calling any method on java.lang.System within the VM + * @param text + * the script to evaluate typed as a string + * @throws SecurityException + * most likely the script is doing something other than arithmetic + * @throws IllegalStateException + * if the script returns something other than a number + */ + def evaluate(String text) { + try { + final SecureASTCustomizer secure = new SecureASTCustomizer() + secure.with { + + receiversClassesBlackList = [ + Object, + Script, + GroovyShell, + Eval, + System, + ].asImmutable() + + expressionsBlacklist = [MethodPointerExpression].asImmutable() + + } + CompilerConfiguration config = new CompilerConfiguration() + config.addCompilationCustomizers(secure) + GroovyClassLoader loader = new GroovyClassLoader(this.class.classLoader, config) + Class clazz = loader.parseClass(text) + Script script = (Script) clazz.newInstance(); + Object result = script.run() + return result + } catch (SecurityException ex) { + throw new SecurityException("Could not evaluate script: $text", ex) + } catch (MultipleCompilationErrorsException mce) { + //this allows compilation errors to be seen by the user + mce.errorCollector.errors.each { + if (it instanceof ExceptionMessage && it.cause instanceof SecurityException) { + throw it.cause + } + } + throw mce + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/groovyShell/BlacklistingShellTest.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovyShell/BlacklistingShellTest.groovy b/src/main/groovy/groovyShell/BlacklistingShellTest.groovy new file mode 100644 index 0000000..e24e149 --- /dev/null +++ b/src/main/groovy/groovyShell/BlacklistingShellTest.groovy @@ -0,0 +1,82 @@ +/* + * 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. + */ +/** +* Unit test for BlacklistingShell. +* Requires JUnit to be in path, just like any other GroovyTestCase. +* +* @author Jim Driscoll [email protected] +*/ +class BlacklistingShellTest extends GroovyTestCase { + + private BlacklistingShell shell + + void setUp() { + shell = new BlacklistingShell() + } + + Object evaluate(String text) { + shell.evaluate(text) + } + + void testEvaluate_SuccessfulPaths() { + assert 6 == evaluate("++(5)") + assert 0 == evaluate("5 < 4 ? 1 : 0") + assert 0 == evaluate("5 != 4 ? 0 : 1") + assert 0 == evaluate("5 < 4 ?: 0 ") + } + + void testEvaluate_Failures() { + shouldFail(SecurityException) { + evaluate('def c = System.class; c.forName("java.lang.System").exit(0)') + } + shouldFail(SecurityException) { + evaluate('Class.forName("java.lang.System").exit(0)') + } + shouldFail(SecurityException) { + evaluate('System.exit(0)') + } + shouldFail(SecurityException) { + evaluate('def e = System.&exit; e.call(0)') + } + shouldFail(SecurityException) { + evaluate('System.&exit.call(0)') + } + shouldFail(SecurityException) { + evaluate('System.getMetaClass().invokeMethod("exit",0)') + } + shouldFail(SecurityException) { + evaluate('evaluate("System.exit(0)")') + } + shouldFail(SecurityException) { + evaluate('(new GroovyShell()).evaluate("System.exit(0)")') + } + shouldFail(SecurityException) { + evaluate('def sh = new GroovyShell(); sh.evaluate("System.exit(0)")') + } + shouldFail(SecurityException) { + evaluate('Eval.me("System.exit(0)")') + } + shouldFail(SecurityException) { + evaluate('def s = System; s.exit(0)') + } + shouldFail(SecurityException) { + evaluate('Script t = this; t.evaluate("System.exit(0)")') + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DefaultGrailsDomainClassInjector.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DefaultGrailsDomainClassInjector.java b/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DefaultGrailsDomainClassInjector.java new file mode 100644 index 0000000..848ebf3 --- /dev/null +++ b/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DefaultGrailsDomainClassInjector.java @@ -0,0 +1,264 @@ +/* + * 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.codehaus.groovy.grails.compiler.injection; + +//import org.apache.commons.logging.Log; +//import org.apache.commons.logging.LogFactory; + +import org.codehaus.groovy.ast.*; +import org.codehaus.groovy.ast.expr.*; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.control.CompilePhase; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.transform.ASTTransformation; +import org.codehaus.groovy.transform.GroovyASTTransformation; + +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.*; + +/** + * This is substantially the same code from Grails, except some references de-referenced + * and the macro class added. + * + * Default implementation of domain class injector interface that adds the 'id' + * and 'version' properties and other previously boilerplate code + * + * @author Graeme Rocher + * + * @since 0.2 + * + * Created: 20th June 2006 + */ +@GroovyASTTransformation(phase= CompilePhase.CANONICALIZATION) +public class DefaultGrailsDomainClassInjector implements ASTTransformation { + //GrailsDomainClassInjector { + + //private static final Log LOG = LogFactory.getLog(DefaultGrailsDomainClassInjector.class); + + + public void visit(ASTNode[] nodes, SourceUnit source) { + performInjection((ClassNode) nodes[1]); + } + + public void performInjection(ClassNode classNode) { + if(shouldInject(classNode)) { + injectIdProperty(classNode); + + injectVersionProperty(classNode); + + injectToStringMethod(classNode); + + injectAssociations(classNode); + + } + } + + public boolean shouldInject(URL url) { + return true; //return GrailsResourceUtils.isDomainClass(url); + } + + private boolean shouldInject(ClassNode classNode) { + //String fullName = GrailsASTUtils.getFullName(classNode); + //String mappingFile = GrailsDomainConfigurationUtil.getMappingFileName(fullName); + + //if(getClass().getResource(mappingFile)!=null) { + //if(LOG.isDebugEnabled()) { + //LOG.debug("[GrailsDomainInjector] Mapping file ["+mappingFile+"] found. Skipping property injection."); + //} + //return false; + //} + return true; + } + + private void injectAssociations(ClassNode classNode) { + + List properties = classNode.getProperties(); + List propertiesToAdd = new ArrayList(); + for (Iterator p = properties.iterator(); p.hasNext();) { + PropertyNode pn = (PropertyNode) p.next(); + final boolean isHasManyProperty = pn.getName().equals(/*GrailsDomainClassProperty.*/RELATES_TO_MANY) || pn.getName().equals(/*GrailsDomainClassProperty.*/HAS_MANY); + if(isHasManyProperty) { + Expression e = pn.getInitialExpression(); + propertiesToAdd.addAll(createPropertiesForHasManyExpression(e,classNode)); + } + final boolean isBelongsTo = pn.getName().equals(/*GrailsDomainClassProperty.*/BELONGS_TO); + if(isBelongsTo) { + Expression e = pn.getInitialExpression(); + propertiesToAdd.addAll(createPropertiesForBelongsToExpression(e,classNode)); + } + } + injectAssociationProperties(classNode,propertiesToAdd); + } + + private Collection createPropertiesForBelongsToExpression(Expression e, ClassNode classNode) + { + List properties = new ArrayList(); + if(e instanceof MapExpression) { + MapExpression me = (MapExpression)e; + List mapEntries = me.getMapEntryExpressions(); + for (Iterator i = mapEntries.iterator(); i.hasNext();) { + MapEntryExpression mme = (MapEntryExpression) i.next(); + String key = mme.getKeyExpression().getText(); + + String type = mme.getValueExpression().getText(); + + properties.add(new PropertyNode(key,Modifier.PUBLIC, ClassHelper.make(type) , classNode, null,null,null)); + } + } + + return properties; + } + + private void injectAssociationProperties(ClassNode classNode, List propertiesToAdd) { + for (Iterator i = propertiesToAdd.iterator(); i.hasNext();) { + PropertyNode pn = (PropertyNode) i.next(); + if(!/*GrailsASTUtils.*/hasProperty(classNode, pn.getName())) { + //if(LOG.isDebugEnabled()) { + // LOG.debug("[GrailsDomainInjector] Adding property [" + pn.getName() + "] to class [" + classNode.getName() + "]"); + //} + classNode.addProperty(pn); + } + } + } + + private List createPropertiesForHasManyExpression(Expression e, ClassNode classNode) { + List properties = new ArrayList(); + if(e instanceof MapExpression) { + MapExpression me = (MapExpression)e; + List mapEntries = me.getMapEntryExpressions(); + for (Iterator j = mapEntries.iterator(); j.hasNext();) { + MapEntryExpression mee = (MapEntryExpression) j.next(); + Expression keyExpression = mee.getKeyExpression(); + String key = keyExpression.getText(); + addAssociationForKey(key,properties,classNode); + } + } + return properties; + } + + private void addAssociationForKey(String key, List properties, ClassNode classNode) { + properties.add(new PropertyNode(key, Modifier.PUBLIC, new ClassNode(Set.class), classNode,null, null, null)); + } + + private void injectToStringMethod(ClassNode classNode) { + final boolean hasToString = /*GrailsASTUtils.*/implementsZeroArgMethod(classNode, "toString"); + + if(!hasToString) { + GStringExpression ge = new GStringExpression(classNode.getName() + " : ${id}"); + ge.addString(new ConstantExpression(classNode.getName()+" : ")); + ge.addValue(new VariableExpression("id")); + Statement s = new ReturnStatement(ge); + MethodNode mn = new MethodNode("toString",Modifier.PUBLIC,new ClassNode(String.class), new Parameter[0],new ClassNode[0],s); + //if(LOG.isDebugEnabled()) { + // LOG.debug("[GrailsDomainInjector] Adding method [toString()] to class [" + classNode.getName() + "]"); + //} + classNode.addMethod(mn); + } + } + + private void injectVersionProperty(ClassNode classNode) { + final boolean hasVersion = /*GrailsASTUtils.*/hasProperty(classNode, /*GrailsDomainClassProperty.*/VERSION); + + if(!hasVersion) { + //if(LOG.isDebugEnabled()) { + // LOG.debug("[GrailsDomainInjector] Adding property [" + GrailsDomainClassProperty.VERSION + "] to class [" + classNode.getName() + "]"); + //} + classNode.addProperty( /*GrailsDomainClassProperty.*/VERSION, Modifier.PUBLIC, new ClassNode(Long.class), null, null, null); + } + } + + private void injectIdProperty(ClassNode classNode) { + final boolean hasId = /*GrailsASTUtils.*/hasProperty(classNode,/*GrailsDomainClassProperty.*/IDENTITY); + + if(!hasId) { + //if(LOG.isDebugEnabled()) { + // LOG.debug("[GrailsDomainInjector] Adding property [" + GrailsDomainClassProperty.IDENTITY + "] to class [" + classNode.getName() + "]"); + //} + classNode.addProperty( /*GrailsDomainClassProperty.*/IDENTITY, Modifier.PUBLIC, new ClassNode(Long.class), null, null, null); + } + } + + + //*************************************************************** + // from GrailsASTUtils + //*************************************************************** + /** + * Returns whether a classNode has the specified property or not + * + * @param classNode The ClassNode + * @param propertyName The name of the property + * @return True if the property exists in the ClassNode + */ + public static boolean hasProperty(ClassNode classNode, String propertyName) { + if(classNode == null || propertyName == null || "".equals(propertyName.trim())) + return false; + + List properties = classNode.getProperties(); + for (Iterator i = properties.iterator(); i.hasNext();) { + PropertyNode pn = (PropertyNode) i.next(); + if(pn.getName().equals(propertyName)) + return true; + } + return false; + } + + /** + * Tests whether the ClasNode implements the specified method name + * + * @param classNode The ClassNode + * @param methodName The method name + * @return True if it does implement the method + */ + public static boolean implementsZeroArgMethod(ClassNode classNode, String methodName) { + return implementsMethod(classNode, methodName, new Class[0]); + } + + + /** + * Tests whether the ClassNode implements the specified method name + * + * @param classNode The ClassNode + * @param methodName The method name + * @param argTypes + * @return True if it implements the method + */ + private static boolean implementsMethod(ClassNode classNode, String methodName, Class[] argTypes) { + List methods = classNode.getMethods(); + if (argTypes == null || argTypes.length ==0) { + for (Iterator i = methods.iterator(); i.hasNext();) { + MethodNode mn = (MethodNode) i.next(); + boolean methodMatch = mn.getName().equals(methodName); + if(methodMatch)return true; + // TODO Implement further parameter analysis + } + } + return false; + } + + //*************************************************************** + // from GrailsDomainClassProperty + //*************************************************************** + private static final String RELATES_TO_MANY = "relatesToMany"; + private static final String BELONGS_TO = "belongsTo"; + private static final String HAS_MANY = "hasMany"; + private static final String IDENTITY = "id"; + private static final String VERSION = "version"; +} http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DomainClass.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DomainClass.java b/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DomainClass.java new file mode 100644 index 0000000..3494ede --- /dev/null +++ b/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DomainClass.java @@ -0,0 +1,38 @@ +/* + * 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.codehaus.groovy.grails.compiler.injection; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by IntelliJ IDEA. + * User: Danno + * Date: Jan 30, 2008 + * Time: 8:11:08 PM + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +@GroovyASTTransformationClass("org.codehaus.groovy.grails.compiler.injection.DefaultGrailsDomainClassInjector") +public @interface DomainClass { +} http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/osgi/build.properties ---------------------------------------------------------------------- diff --git a/src/main/groovy/osgi/build.properties b/src/main/groovy/osgi/build.properties new file mode 100644 index 0000000..bb104c4 --- /dev/null +++ b/src/main/groovy/osgi/build.properties @@ -0,0 +1,28 @@ +# +# 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. +# + +# groovy bin dir is required to locate the groovy jar +# in case it's not on the classpath +groovy.bin.dir=../../../target/dist + +# version is required to locate jar file and build jar manifest +groovy.version=1.7-beta-1-SNAPSHOT + +# osgi jar file location must be specified so code compiles +osgi.jar=../../../../equinox-3.4/eclipse/plugins/org.eclipse.osgi_3.4.0.v20080605-1900.jar http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/osgi/build.xml ---------------------------------------------------------------------- diff --git a/src/main/groovy/osgi/build.xml b/src/main/groovy/osgi/build.xml new file mode 100644 index 0000000..9d31efb --- /dev/null +++ b/src/main/groovy/osgi/build.xml @@ -0,0 +1,140 @@ +<?xml version="1.0"?> +<!-- + + 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. + +--> +<project name="groovy-osgi-sample" default="print-results"> + + <property name="build.dir" value="build"/> + <property file="build.properties"/> + <property name="groovy.jar" value="groovy-${groovy.version}.jar" /> + <property name="hello.bundle" value="hello-bundle-imports-groovy.jar" /> + <property name="hello.bundle.contains" value="hello-bundle-contains-groovy.jar" /> + <property name="harness.bundle" value="hello-groovy-test-harness.jar" /> + + <taskdef name="groovyc" + classname="org.codehaus.groovy.ant.Groovyc" + classpathref="project.classpath"/> + + <target name="init" description="cleanup and reinitialize"> + <path id="project.classpath"> + <pathelement location="${osgi.jar}"/> + <pathelement location="${groovy.bin.dir}${file.separator}${groovy.jar}"/> + <pathelement location="${build.dir}${file.separator}${hello.bundle}"/> + </path> + + <delete dir="${build.dir}" /> + <mkdir dir="${build.dir}${file.separator}hello-groovy-bundle" /> + <mkdir dir="${build.dir}${file.separator}hello-groovy-test-harness" /> + </target> + + <target name="make-hello-groovy-bundle" description="compile and build the Hello Groovy bundles"> + + <groovyc destdir="${build.dir}${file.separator}hello-groovy-bundle" + srcdir=".${file.separator}hello-groovy-bundle" + listfiles="true"> + <classpath refid="project.classpath"/> + </groovyc> + + <!-- This jar file imports Groovy from the container --> + <jar destfile="${build.dir}${file.separator}${hello.bundle}"> + <fileset dir="${build.dir}${file.separator}hello-groovy-bundle"/> + <manifest> + <attribute name="Built-By" value="${user.name}"/> + <attribute name="provider" value="org.codehaus.groovy.osgi"/> + <attribute name="Bundle-ManifestVersion" value="2"/> + <attribute name="Bundle-Name" value="Groovy OSGi Example Bundle"/> + <attribute name="Bundle-SymbolicName" value="org.codehaus.groovy.osgi.hello-groovy-bundle"/> + <attribute name="Bundle-Version" value="1.0.0"/> + <attribute name="Bundle-Activator" value="org.codehaus.groovy.osgi.Activator"/> + <attribute name="Bundle-Vendor" value="Groovy"/> + <attribute name="Bundle-Localization" value="plugin"/> + <attribute name="Import-Package" value="groovy.lang;version="1.7.0.beta-1-SNAPSHOT",org.codehaus.groovy.reflection;version="1.7.0.beta-1-SNAPSHOT",org.codehaus.groovy.runtime;version="1.7.0.beta-1-SNAPSHOT",org.codehaus.groovy.runtime.callsite;version="1.7.0.beta-1-SNAPSHOT",org.w3c.dom,org.osgi.framework;version="1.3.0""/> + <attribute name="Export-Package" value="org.codehaus.groovy.osgi;version="1.0.0""/> + <attribute name="Bundle-ClassPath" value="."/> + </manifest> + </jar> + + <!-- This jar includes the Groovy jar within itself--> + <jar destfile="${build.dir}${file.separator}${hello.bundle.contains}"> + <fileset dir="${build.dir}${file.separator}hello-groovy-bundle"/> + <fileset file="${groovy.bin.dir}${file.separator}${groovy.jar}" casesensitive="no" /> + <manifest> + <attribute name="Built-By" value="${user.name}"/> + <attribute name="provider" value="org.codehaus.groovy.osgi"/> + <attribute name="Bundle-ManifestVersion" value="2"/> + <attribute name="Bundle-Name" value="Groovy OSGi Example Bundle"/> + <attribute name="Bundle-SymbolicName" value="org.codehaus.groovy.osgi.hello-groovy-bundle"/> + <attribute name="Bundle-Version" value="1.0.0"/> + <attribute name="Bundle-Activator" value="org.codehaus.groovy.osgi.Activator"/> + <attribute name="Bundle-Vendor" value="Groovy"/> + <attribute name="Bundle-Localization" value="plugin"/> + <attribute name="Import-Package" value="org.w3c.dom,org.osgi.framework;version="1.3.0""/> + <attribute name="Export-Package" value="org.codehaus.groovy.osgi;version="1.0.0""/> + <attribute name="Bundle-ClassPath" value=".,${groovy.jar}"/> + </manifest> + </jar> + </target> + + <target name="make-harness-bundle" description="Makes the test harness bundle"> + + <groovyc destdir="${build.dir}${file.separator}hello-groovy-test-harness" + srcdir=".${file.separator}hello-groovy-test-harness" + listfiles="true"> + <classpath refid="project.classpath"/> + </groovyc> + + <jar destfile="${build.dir}${file.separator}${harness.bundle}"> + <fileset dir="${build.dir}${file.separator}hello-groovy-test-harness"/> + <manifest> + <attribute name="Built-By" value="${user.name}"/> + <attribute name="provider" value="org.codehaus.groovy.osgi.harness"/> + <attribute name="Bundle-ManifestVersion" value="2"/> + <attribute name="Bundle-Name" value="Groovy OSGi Test Harness"/> + <attribute name="Bundle-SymbolicName" value="org.codehaus.groovy.osgi.harness.hello-groovy-test-harness"/> + <attribute name="Bundle-Version" value="1.0.0"/> + <attribute name="Bundle-Activator" value="org.codehaus.groovy.osgi.harness.HarnessActivator"/> + <attribute name="Bundle-Vendor" value="Groovy"/> + <attribute name="Bundle-Localization" value="plugin"/> + <attribute name="Import-Package" value="org.codehaus.groovy.runtime.typehandling;version="1.0.0",org.codehaus.groovy.osgi;version="1.0.0",groovy.lang;version="1.7.0.beta-1-SNAPSHOT",org.codehaus.groovy.reflection;version="1.7.0.beta-1-SNAPSHOT",org.codehaus.groovy.runtime;version="1.7.0.beta-1-SNAPSHOT",org.codehaus.groovy.runtime.callsite;version="1.7.0.beta-1-SNAPSHOT",org.w3c.dom,org.osgi.framework;version="1.3.0""/> + <attribute name="Bundle-ClassPath" value="."/> + </manifest> + </jar> + + </target> + + <target name="print-results" depends="init,make-hello-groovy-bundle,make-harness-bundle" description="compile and build everything"> + <makeurl file="${basedir}/${groovy.bin.dir}/${groovy.jar}" property="groovy.jar.url"/> + <makeurl file="${basedir}/${build.dir}/${hello.bundle}" property="hello.jar1.url"/> + <makeurl file="${basedir}/${build.dir}/${hello.bundle.contains}" property="hello.jar2.url"/> + <makeurl file="${basedir}/${build.dir}/${harness.bundle}" property="harness.jar.url"/> + + <echo>To run the OSGi console, run the following command: + java -jar ${osgi.jar} -console +To install these applications in the container, run the following commands in the OSGi container: + install ${groovy.jar.url} + install ${hello.jar1.url} + install ${hello.jar2.url} + install ${harness.jar.url} +To start the applications in the container, run the following commands in the OSGi container: + start [bundle1] [bundle2] +Where [bundle1] and [bundle] are the bundle IDs printed by the console in the previous step.</echo> + </target> +</project> + http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/Activator.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/Activator.groovy b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/Activator.groovy new file mode 100644 index 0000000..e250ec1 --- /dev/null +++ b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/Activator.groovy @@ -0,0 +1,59 @@ +/* + * 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.codehaus.groovy.osgi + +import org.osgi.framework.BundleActivator +import org.osgi.framework.BundleContext +import org.osgi.framework.ServiceRegistration; + +/** +* This is the OSGi Activator for the Groovy example bundles. +* Two things happen when the container starts this bundle: +* 1) a message is printed to standard out +* 2) a service of type GroovyGreeter is added to the context +* The service is unregistered when the bundle is stopped. +* +* @author Hamlet D'Arcy +*/ +public class Activator implements BundleActivator { + + ServiceRegistration registration + + public void start(BundleContext context) { + println "Groovy BundleActivator started" + + // Normally, the classloader code would not need to be run when + // adding a service to the context. However, this is required when + // adding a Groovy service because of the way Groovy uses class + // loaders and reflection. + ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader() + try { + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()) + GroovyGreeter myService = new GroovyGreeterImpl() + registration = context.registerService(GroovyGreeter.class.getName(), myService, null) + } finally { + Thread.currentThread().setContextClassLoader(originalClassLoader) + } + } + + public void stop(BundleContext context) { + println "Groovy BundleActivator stopped" + registration.unregister(); + } +} http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeter.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeter.groovy b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeter.groovy new file mode 100644 index 0000000..4409cd4 --- /dev/null +++ b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeter.groovy @@ -0,0 +1,30 @@ +/* + * 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.codehaus.groovy.osgi + +/** +* Objects of this type know how to print simple messages to +* standard out. +* +* @author Hamlet D'Arcy +*/ +public interface GroovyGreeter { + + void sayHello(); +} http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeterImpl.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeterImpl.groovy b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeterImpl.groovy new file mode 100644 index 0000000..209871e --- /dev/null +++ b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeterImpl.groovy @@ -0,0 +1,31 @@ +/* + * 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.codehaus.groovy.osgi + +/** +* A simple POGO that prints a greeting to standard out. +* +* @author Hamlet D'Arcy +*/ +public class GroovyGreeterImpl implements GroovyGreeter { + + public void sayHello() { + println "Hello from the Groovy Greeter!" + } +} http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/osgi/hello-groovy-test-harness/org/codehaus/groovy/osgi/harness/HarnessActivator.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/osgi/hello-groovy-test-harness/org/codehaus/groovy/osgi/harness/HarnessActivator.groovy b/src/main/groovy/osgi/hello-groovy-test-harness/org/codehaus/groovy/osgi/harness/HarnessActivator.groovy new file mode 100644 index 0000000..adccf08 --- /dev/null +++ b/src/main/groovy/osgi/hello-groovy-test-harness/org/codehaus/groovy/osgi/harness/HarnessActivator.groovy @@ -0,0 +1,50 @@ +/* + * 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.codehaus.groovy.osgi.harness + +import org.osgi.framework.BundleActivator +import org.osgi.framework.BundleContext +import org.osgi.framework.ServiceRegistration +import org.osgi.framework.ServiceReference +import org.codehaus.groovy.osgi.GroovyGreeter + +/** + * This OSGi Activator finds all registered services of type GroovyGreeter + * and then invokes the sayHello() method on any that it finds. + * + * @author Hamlet D'Arcy + */ +public class HarnessActivator implements BundleActivator { + + public void start(BundleContext context) { + String serviceName = GroovyGreeter.class.getName() + ServiceReference[] references = context.getAllServiceReferences(serviceName, null) + + println "${ references ? references.size() : 0 } GroovyGreeter services found." + + references?.each { ServiceReference ref -> + Object serviceHandle = context.getService(ref) + GroovyGreeter service = (GroovyGreeter) serviceHandle + service.sayHello() + } + } + + public void stop(BundleContext context) { + } +} http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/osgi/readme.txt ---------------------------------------------------------------------- diff --git a/src/main/groovy/osgi/readme.txt b/src/main/groovy/osgi/readme.txt new file mode 100644 index 0000000..7ee164c --- /dev/null +++ b/src/main/groovy/osgi/readme.txt @@ -0,0 +1,140 @@ +==== + 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. +==== + +About this Example +------------------ +This example demonstrates two different usages of OSGi. The "hello-groovy-bundle" +service demonstrates how to write an OSGi bundle using Groovy. It is a simple +service that does two things when started within an OSGi container: 1) it prints +out a message to the console, and 2) it adds a Groovy service to the OSGi context +that can be consumed by the second example. + +The "hello-groovy-test-harness" service, also written in Groovy, demonstrates how +to import and use the previous "hello-groovy-bundle" OSGi service. It locates and +invokes the service from the first example, which results in a message being +written to the console. + + +Building this Example +--------------------- +IMPORTANT: You must edit build.properties before building the example. There are +three properties that must be set in build.properties: + groovy.bin.dir - The example requires you specify the location of your + groovy jar. This is how the build finds groovyc. + groovy.version - The example requires you specify the version of your groovy + jar. This is so that the jar files can be built correctly. + osgi.jar - The example requires you specify the location of the OSGi jar. + This is required to compile the code. + +This example was tested using the OSGi jar from Equinox 3.4, the OSGi container +that ships with Eclipse. You can download the Equinox jar from the Equinox website +or search for it within your Eclipse directories. The jar will have a name similar +to : org.eclipse.osgi_3.4.0.v20080605-1900.jar + +Once these properties are set, simply run ant to build: + + ant + +The build creates three jar files: + hello-bundle-imports-groovy.jar - OSGi bundle written in Groovy that resolves the + groovy Jar file from the container. + hello-bundle-contains-groovy.jar - OSGi bundle written in Groovy that resolves the + groovy Jar file from within itself. The container never sees Groovy. + hello-groovy-test-harness.jar - OSGi bundle that loads and tests one of the previous + two services. + +The build also prints out the file URLs of the jar files. You need these URLs to +run the example. Also printed to the console is the command to run the Equinox +container. The final output of the Ant script may look like this: + + [echo] To run the OSGi console, run the following command: + [echo] java -jar ../../../../equinox-3.4/eclipse/plugins/org.eclipse.osgi_3.4.0.v20080605-1900.jar -console + [echo] To install these applications in the container, run the following commands in the OSGi container: + [echo] install file:/home/user/dev/groovy-core/target/dist/groovy-all-1.7.0.jar + [echo] install file:/home/user/dev/groovy-core/src/examples/osgi/build/hello-bundle-imports-groovy.jar + [echo] install file:/home/user/dev/groovy-core/src/examples/osgi/build/hello-bundle-contains-groovy.jar + [echo] install file:/home/user/dev/groovy-core/src/examples/osgi/build/hello-groovy-test-harness.jar + [echo] To start the applications in the container, run the following commands in the OSGi container: + [echo] start [bundle1] [bundle2] + [echo] Where [bundle1] and [bundle] are the bundle IDs printed by the console in the previous step. + + +Running this Example +-------------------- +To run the example you must start the OSGi container, install the services, and +start the services. + +To start the Equinox container, invoke the OSGi jar using java: + + java -jar ../../../../equinox-3.4/eclipse/plugins/org.eclipse.osgi_3.4.0.v20080605-1900.jar -console + +This opens an OSGi console. You should be presented with an OSGi prompt: + + osgi> + +Type the command "ss" to get a system status: + + osgi> ss + + Framework is launched. + + id State Bundle + 0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900 + +Install the three bundles using the "install" command and the file URLs of the +jars built by Ant. Remember, the Ant script printed the file URLs to the console +as part of the build (replace groovy-all with groovy for 2.5+). + +osgi> install file:/home/user/dev/groovy-core/target/dist/groovy-all-1.7.0.jar +Bundle id is 1 + +osgi> install file:/home/user/dev/groovy-core/src/examples/osgi/build/hello-bundle-imports-groovy.jar +Bundle id is 2 + +osgi> install file:/home/user/dev/groovy-core/src/examples/osgi/build/hello-groovy-test-harness.jar +Bundle id is 3 + +Run the ss command to verify the bundles loaded correctly: + + +osgi> ss + +Framework is launched. + +id State Bundle +0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900 +1 INSTALLED groovy-all_1.7.0 +2 INSTALLED org.codehaus.groovy.osgi.hello-groovy-bundle_1.0.0 +3 INSTALLED org.codehaus.groovy.osgi.harness.hello-groovy-test-harness_1.0.0 + +Start the bundles with the "start" command to see them working: + +osgi> start 1 2 3 +Groovy BundleActivator started +1 GroovyGreeter services found. +Hello from the Groovy Greeter! + +As expected, bundle 2 printed out a message from an object implemented in Groovy, +and bundle 3 printed out a message from a service implemented in Groovy, which it +loaded as an OSGi service from the BundleContext. + +You may wish to uninstall the services using the "uninstall" command: + + osgi> uninstall 3 2 1 + Groovy BundleActivator stopped http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/searchEngine/Indexer.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/searchEngine/Indexer.groovy b/src/main/groovy/searchEngine/Indexer.groovy new file mode 100644 index 0000000..2709e6a --- /dev/null +++ b/src/main/groovy/searchEngine/Indexer.groovy @@ -0,0 +1,83 @@ +/* + * 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. + */ +import org.apache.lucene.analysis.standard.StandardAnalyzer +import org.apache.lucene.document.Document +import org.apache.lucene.document.Field +import org.apache.lucene.index.IndexWriter +import org.apache.lucene.util.Version +import org.apache.lucene.index.IndexWriterConfig +import org.apache.lucene.store.FSDirectory +import org.apache.lucene.document.TextField +import org.apache.lucene.document.StringField +import static org.apache.lucene.document.Field.Store.* + +/** + * Indexer: traverses a file system and indexes .txt files + * + * @author Jeremy Rayner <[email protected]> + * based on examples in the wonderful 'Lucene in Action' book + * by Erik Hatcher and Otis Gospodnetic (https://www.manning.com/books/lucene-in-action-second-edition) + * + * June 25th, 2013: Updated for Lucene 4.3.1 + * requires a lucene-4.3.x.jar from http://lucene.apache.org + */ + +if (args.size() != 2 ) { + throw new Exception("Usage: groovy -cp lucene-1.4.3.jar Indexer <index dir> <data dir>") +} +def indexDir = FSDirectory.open(new File(args[0])) // Create Lucene index in this directory +def dataDir = new File(args[1]) // Index files in this directory + +def start = new Date().time +def numIndexed = index(indexDir, dataDir) +def end = new Date().time + +println "Indexing $numIndexed files took ${end - start} milliseconds" + +def index(indexDir, dataDir) { + if (!dataDir.exists() || !dataDir.directory) { + throw new IOException("$dataDir does not exist or is not a directory") + } + def config = new IndexWriterConfig(Version.LUCENE_43, new StandardAnalyzer(Version.LUCENE_43)) + def writer = new IndexWriter(indexDir, config) // Create Lucene index + + dataDir.eachFileRecurse { + if (it.name =~ /.txt$/) { // Index .txt files only + indexFile(writer,it) + } + } + def numIndexed = writer.numDocs() + writer.close() // Close index + return numIndexed +} + +void indexFile(writer, f) { + if (f.hidden || !f.exists() || !f.canRead() || f.directory) { return } + + println "Indexing $f.canonicalPath" + def doc = new Document() + + // Construct a Field that is tokenized and indexed, but is not stored in the index verbatim. + doc.add(new TextField("contents", f.newReader())) + + // Construct a Field that is not tokenized, but is indexed and stored. + doc.add(new StringField("filename",f.canonicalPath, YES)) + + writer.addDocument(doc) // Add document to Lucene index +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/searchEngine/Searcher.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/searchEngine/Searcher.groovy b/src/main/groovy/searchEngine/Searcher.groovy new file mode 100644 index 0000000..4afd145 --- /dev/null +++ b/src/main/groovy/searchEngine/Searcher.groovy @@ -0,0 +1,61 @@ +/* + * 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. + */ +import org.apache.lucene.analysis.standard.StandardAnalyzer +import org.apache.lucene.queryparser.classic.QueryParser +import org.apache.lucene.search.IndexSearcher +import org.apache.lucene.store.FSDirectory +import org.apache.lucene.util.Version +import org.apache.lucene.index.DirectoryReader + +/** + * Searcher: searches a Lucene index for a query passed as an argument + * + * @author Jeremy Rayner <[email protected]> + * based on examples in the wonderful 'Lucene in Action' book + * by Erik Hatcher and Otis Gospodnetic (https://www.manning.com/books/lucene-in-action-second-edition) + * + * June 25th, 2013: Updated for Lucene 4.3.1 + * requires a lucene-4.x.x.jar from http://lucene.apache.org + */ + +if (args.size() != 2) { + throw new Exception("Usage: groovy -cp lucene-4.3.1.jar Searcher <index dir> <query>") +} +def indexDir = new File(args[0]) // Index directory create by Indexer +def q = args[1] // Query string + +if (!indexDir.exists() || !indexDir.directory) { + throw new Exception("$indexDir does not exist or is not a directory") +} + +def fsDir = DirectoryReader.open(FSDirectory.open(indexDir)) +def is = new IndexSearcher(fsDir) // Open index + +def parser = new QueryParser(Version.LUCENE_43, "contents", new StandardAnalyzer(Version.LUCENE_43)) +def query = parser.parse(q) // Parse query +def start = new Date().time +def hits = is.search(query, 10) // Search index +def end = new Date().time + +println "Found ${hits.totalHits} document(s) (in ${end - start} milliseconds) that matched query '$q':" + +hits.scoreDocs.each { scoreDoc -> + println(is.doc(scoreDoc.doc)["filename"]) // Retrieve matching document and display filename +} +fsDir.close() http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/swing/BindingExample.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/swing/BindingExample.groovy b/src/main/groovy/swing/BindingExample.groovy new file mode 100644 index 0000000..3cdcb60 --- /dev/null +++ b/src/main/groovy/swing/BindingExample.groovy @@ -0,0 +1,63 @@ +/* + * 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. + */ +/** + * @author <a href="mailto:[email protected]">Danno Ferrin</a> + * @since Groovy 1.1 + * + * The real interesting part of this example are in the three properties of button: + * + * text: bind(source:textField, sourceProperty:'text'), + * margin: bind(source:slider, sourceProperty:'value', converter:{[it, it, it, it] as Insets}), + * enabled: bind(source:checkBox, sourceProperty:'selected') + * + * This is where the real magic goes on, causing the button to react to the changes + * in the source widgets values. + */ + package swing + +import groovy.swing.SwingBuilder +import java.awt.GridBagConstraints as gb +import java.awt.Insets + +sb = SwingBuilder.build() { + frame = frame(defaultCloseOperation:javax.swing.JFrame.DISPOSE_ON_CLOSE) { + gridBagLayout() + + label("Text:", anchor:gb.WEST, insets:[6,6,3,3] as Insets) + textField = textField("Change Me!", fill:gb.HORIZONTAL, gridwidth:gb.REMAINDER, insets:[6,3,3,6] as Insets) + + label("Margin:", anchor:gb.WEST, insets:[3,6,3,3] as Insets) + slider = slider(value:5, fill:gb.HORIZONTAL, gridwidth:gb.REMAINDER, insets:[3,3,3,6] as Insets) + + panel() + checkBox = checkBox("Enbled", anchor:gb.WEST, gridwidth:gb.REMAINDER, insets:[3,3,3,6] as Insets) + + separator(fill:gb.HORIZONTAL, gridwidth:gb.REMAINDER) + + button(anchor:gb.CENTER, gridwidth:gb.REMAINDER, gridheight:gb.REMAINDER, weightx:1.0, weighty:1.0, insets:[3,6,6,6] as Insets, + text: bind(source:textField, sourceProperty:'text'), + margin: bind(source:slider, sourceProperty:'value', converter:{[it, it, it, it] as Insets}), + enabled: bind(source:checkBox, sourceProperty:'selected') + ) + } +} + +frame.pack() +frame.setSize(frame.width + 100, frame.height + 200) +frame.show() http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/swing/BloglinesClient.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/swing/BloglinesClient.groovy b/src/main/groovy/swing/BloglinesClient.groovy new file mode 100644 index 0000000..017ef0a --- /dev/null +++ b/src/main/groovy/swing/BloglinesClient.groovy @@ -0,0 +1,188 @@ +/* + * 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. + */ +/* + * BloglinesClient.groovy - an example of the Bloglines Web Services + * + * Written by Marc Hedlund <[email protected]>, September 2004. + * + * Mangled by John Wilson September 2004 + * + * Small adaptions to JSR Version by Dierk Koenig, June 2005 + * + * Used in Marc's article at: + * http://www.oreillynet.com/pub/a/network/2004/09/28/bloglines.html + * + * Requirements: + * - install Groovy as detailed at <http://groovy.codehaus.org/>. + * - put commons-httpclient-3.0-rc3.jar into GROOVY_HOME/lib + * see <http://jakarta.apache.org/commons/httpclient/>. + * note: this is currently designed for HttpClient2.x and not HttpClient3.x + * + * To Launch: + * groovy BloglinesClient.groovy + * + * This work is licensed under the Creative Commons Attribution + * License. To view a copy of this license, visit + * <http://creativecommons.org/licenses/by/2.0/> or send a letter to + * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + */ +package swing + +import groovy.swing.SwingBuilder +import java.awt.BorderLayout +import javax.swing.JOptionPane +import javax.swing.JSplitPane +import javax.swing.JTree +import javax.swing.ListSelectionModel +import javax.swing.WindowConstants +import javax.swing.tree.DefaultMutableTreeNode +import javax.swing.tree.TreeSelectionModel +import org.apache.commons.httpclient.HttpClient +import org.apache.commons.httpclient.UsernamePasswordCredentials +import org.apache.commons.httpclient.methods.GetMethod + +//Set up global variables and data types +server = 'rpc.bloglines.com' + +class Feed { + def name; + def id; + def unread; + + String toString() { (unread == "0" ? name : "${name} (${unread})") } +} + +class Item { + def title; + def description; + + String toString() { title } +} + +// Ask the user for account information (using simple dialogs) +email = JOptionPane.showInputDialog(null, "Email address:", "Log in to Bloglines", + JOptionPane.QUESTION_MESSAGE) +password = JOptionPane.showInputDialog(null, "Password:", "Log in to Bloglines", + JOptionPane.QUESTION_MESSAGE) + +//Use HTTPClient for web requests since the server requires authentication +client = new HttpClient() +credentials = new UsernamePasswordCredentials(email, password) +client.state.setCredentials("Bloglines RPC", server, credentials) + +abstractCallBloglines = { method, parameters -> + url = "http://${server}/${method}${parameters}" + try { + get = new GetMethod(url) + get.doAuthentication = true + client.executeMethod(get) + return get.responseBodyAsStream + } catch (Exception e) { + println "Error retrieving <${url}>: ${e}" + } +} + +callBloglinesListsub = abstractCallBloglines.curry('listsubs', '') +callBloglinesGetItems = abstractCallBloglines.curry('getitems') + +//Get the list of subscriptions and parse it into a GPath structure +opml = new XmlSlurper().parse(callBloglinesListsub()) + +//Descend into the subscription outline, adding to the feed tree as we go +treeTop = new DefaultMutableTreeNode("My Feeds") +parseOutline(opml.body.outline.outline, treeTop) + +def parseOutline(parsedXml, treeLevel) { + parsedXml.each { outline -> + if (outline['@xmlUrl'] != null) { // this is an individual feed + feed = new Feed(name: outline['@title'], id: outline['@BloglinesSubId'], + unread: outline['@BloglinesUnread']) + treeLevel.add(new DefaultMutableTreeNode(feed)) + } else { // this is a folder of feeds + folder = new DefaultMutableTreeNode(outline['@title']) + parseOutline(outline.outline, folder) + treeLevel.add(folder) + } + } +} + +//Build the base user interface objects and configure them +swing = new SwingBuilder() +feedTree = new JTree(treeTop) +itemList = swing.list() +itemText = swing.textPane(contentType: 'text/html', editable: false) +model = feedTree.selectionModel +model.selectionMode = TreeSelectionModel.SINGLE_TREE_SELECTION +itemList.selectionMode = ListSelectionModel.SINGLE_SELECTION + +//Set up the action closures that will react to user selections +listItems = { feed -> + rssStream = callBloglinesGetItems("?s=${feed.id}&n=0") + if (rssStream != null) { + try { + rss = new XmlSlurper().parse(rssStream) + itemList.listData = rss.channel.item.collect(new Vector()) { + new Item(title: it.title, description: it.description) + } + feed.unread = "0" // update the unread item count in the feed list + } catch (Exception e) { + println "Error during <${feed.name}> RSS parse: ${e}" + } + } +} + +feedTree.valueChanged = { event -> + itemText.text = "" // clear any old item text + node = (DefaultMutableTreeNode) feedTree.getLastSelectedPathComponent() + if (node != null) { + feed = node.userObject + if (feed instanceof Feed && feed.unread != "0") { + listItems(feed) + } + } +} + +itemList.valueChanged = { event -> + item = event.source.selectedValue + if (item instanceof Item && item?.description != null) { + itemText.text = "<html><body>${item.description}</body></html>" + } +} + +//Put the user interface together and display it +gui = swing.frame(title: 'Bloglines Client', location: [100, 100], size: [800, 600], + defaultCloseOperation: WindowConstants.EXIT_ON_CLOSE) { + panel(layout: new BorderLayout()) { + splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT, dividerLocation: 200) { + scrollPane { + widget(feedTree) + } + splitPane(orientation: JSplitPane.VERTICAL_SPLIT, dividerLocation: 150) { + scrollPane(constraints: BorderLayout.CENTER) { + widget(itemList) + } + scrollPane(constraints: BorderLayout.CENTER) { + widget(itemText) + } + } + } + } +} + +gui.show() http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/swing/ModelNodeExample.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/swing/ModelNodeExample.groovy b/src/main/groovy/swing/ModelNodeExample.groovy new file mode 100644 index 0000000..da52139 --- /dev/null +++ b/src/main/groovy/swing/ModelNodeExample.groovy @@ -0,0 +1,89 @@ +/* + * 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 swing + +import groovy.swing.SwingBuilder +import static javax.swing.WindowConstants.* +import static java.awt.GridBagConstraints.* + +def bean = new ObservableMap([name:'Alice', phone:'719-555-1212', addr:'42 Other Way']) + +SwingBuilder.build { + frame = frame( + pack:true, + show:true, + defaultCloseOperation:DISPOSE_ON_CLOSE) + { + beanModel = model(bean, bind:false) + + gridBagLayout() + + label('Name:', constraints:gbc(insets:[6,6,3,3])) + textField(text:beanModel.name, + columns:20, + gridwidth:REMAINDER, + fill:HORIZONTAL, + weightx:1, + insets:[6,3,3,6]) + + label('Phone:', constraints:gbc(insets:[3,6,3,3])) + textField(text:beanModel.phone, + columns:20, + gridwidth:REMAINDER, + fill:HORIZONTAL, + weightx:1, + insets:[3,3,3,6]) + + label('Address:', constraints:gbc(insets:[3,6,3,3])) + textField(text:beanModel.addr, + columns:20, + gridwidth:REMAINDER, + fill:HORIZONTAL, + weightx:1, + insets:[3,3,3,6]) + + button('Reset', actionPerformed:{beanModel.update()}, + constraints:gbc(gridwidth:2, + anchor:EAST, + weightx:1, + insets:[9,0,0,6])) + button('Submit', + insets:[9,0,0,0], + actionPerformed: { + beanModel.reverseUpdate() + output.text = ("name = '$bean.name'\nphone = '$bean.phone'\naddr = '$bean.addr'\n\n") + }) + + separator(gridwidth:REMAINDER, + fill:HORIZONTAL, + insets:[3,6,3,6]) + label('Output:', + gridwidth:REMAINDER, + anchor:WEST, + insets:[3,6,3,6]) + scrollPane(preferredSize:[100, 100], + gridwidth:REMAINDER, + fill:BOTH, + weighty:1, + insets:[3,6,6,6]) + { + output = textArea() + } + } +} http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/swing/RegexCoach.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/swing/RegexCoach.groovy b/src/main/groovy/swing/RegexCoach.groovy new file mode 100644 index 0000000..e282116 --- /dev/null +++ b/src/main/groovy/swing/RegexCoach.groovy @@ -0,0 +1,126 @@ +/* + * 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. + */ +// inspired by http://weitz.de/regex-coach/ +package swing + +import java.awt.* +import java.awt.event.* +import java.util.regex.* +import javax.swing.* +import javax.swing.text.DefaultHighlighter +import groovy.swing.SwingBuilder + +// define the view +def swing = new SwingBuilder() +def gui = swing.frame(title: 'The Groovy Regex Coach', location: [20, 40], size: [600, 500], defaultCloseOperation: WindowConstants.EXIT_ON_CLOSE) { + panel(layout: new BorderLayout()) { + splitPane(orientation: JSplitPane.VERTICAL_SPLIT, dividerLocation: 150) { + panel(layout: new BorderLayout()) { + label(constraints: BorderLayout.NORTH, text: 'Regular expression:') + scrollPane(constraints: BorderLayout.CENTER) {textPane(id: 'regexPane')} + label(constraints: BorderLayout.SOUTH, id: 'regexStatus', text: ' ') + } + panel(layout: new BorderLayout()) { + label(constraints: BorderLayout.NORTH, text: 'Target string:') + scrollPane(constraints: BorderLayout.CENTER) {textPane(id: 'targetPane')} + panel(constraints: BorderLayout.SOUTH, layout: new BorderLayout()) { + label(constraints: BorderLayout.NORTH, id: 'targetStatus', text: ' ') + panel(constraints: BorderLayout.SOUTH, layout: new FlowLayout()) { + button('<<-', id: 'scanLeft') + button('->>', id: 'scanRight') + } + } + } + } + } +} +def highlighter = new RegexHighlighter(swing: swing) +swing.regexPane.addKeyListener(highlighter) +swing.targetPane.addKeyListener(highlighter) +swing.scanLeft.addActionListener(highlighter) +swing.scanRight.addActionListener(highlighter) +gui.show() + +class RegexHighlighter extends KeyAdapter implements ActionListener { + def swing // reference to the view + int scanIndex // how many times to execute matcher.find() + def orange = new DefaultHighlighter.DefaultHighlightPainter(Color.ORANGE) + def yellow = new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW) + def red = new DefaultHighlighter.DefaultHighlightPainter(Color.RED) + + // react to user actions + + public void actionPerformed(ActionEvent event) { + if (event.actionCommand == '<<-') {scanIndex = Math.max(scanIndex - 1, 0)} + if (event.actionCommand == '->>') {scanIndex++} + doHighlights() + } + + public void keyReleased(KeyEvent event) { + scanIndex = 0 + doHighlights() + } + + private resetView() { + swing.regexPane.highlighter.removeAllHighlights() + swing.targetPane.highlighter.removeAllHighlights() + swing.regexStatus.text = ' ' + swing.targetStatus.text = ' ' + } + + // the main regex logic + + private doHighlights() { + try { + resetView() + // note: get the text from the underlying document, + // otherwise carriage return/line feeds different when using the JTextPane text + def regex = swing.regexPane.document.getText(0, swing.regexPane.document.length) + def target = swing.targetPane.document.getText(0, swing.targetPane.document.length) + + def matcher = (target =~ regex) + + // scan past the matches before the match we want + int scan = 0 + while (scan < scanIndex) { + matcher.find() + scan++ + } + if (matcher.find()) { + // highlight any captured groups + int i = 0 + while (i++ < matcher.groupCount()) { + swing.targetPane.highlighter.addHighlight(matcher.start(i), matcher.end(i), orange) + } + // highlight whole match + swing.targetPane.highlighter.addHighlight(matcher.start(), matcher.end(), yellow) + if (regex.length() != 0) { + swing.targetStatus.text = "Match #${scanIndex + 1} from ${matcher.start()} to ${matcher.end()}." + } + } else { // not found + scanIndex = Math.max(scan - 1, 0) + if (scanIndex > 0) {doHighlights()} + swing.targetStatus.text = "No match." + } + } catch (PatternSyntaxException e) { + swing.regexPane.highlighter.addHighlight(e.index, e.index + 2, red) + swing.regexStatus.text = e.description + } + } +} http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/swing/RegexCoachController.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/swing/RegexCoachController.groovy b/src/main/groovy/swing/RegexCoachController.groovy new file mode 100644 index 0000000..bf6cd9d --- /dev/null +++ b/src/main/groovy/swing/RegexCoachController.groovy @@ -0,0 +1,107 @@ +/* + * 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 swing + +import groovy.swing.SwingBuilder +import java.awt.Color +import java.awt.event.ActionEvent +import java.awt.event.ActionListener +import java.awt.event.KeyAdapter +import java.awt.event.KeyEvent +import java.util.regex.PatternSyntaxException +import javax.swing.text.DefaultHighlighter.DefaultHighlightPainter + +// inspired by http://weitz.de/regex-coach/ + +// define the view +def swing = new SwingBuilder() + +def gui = swing.build(RegexCoachView) + +def highlighter = new RegexHighliter(swing: swing) +swing.regexPane.addKeyListener(highlighter) +swing.targetPane.addKeyListener(highlighter) +swing.scanLeft.addActionListener(highlighter) +swing.scanRight.addActionListener(highlighter) +gui.show() + +class RegexHighliter extends KeyAdapter implements ActionListener { + def swing // reference to the view + int scanIndex // how many times to execute matcher.find() + def orange = new DefaultHighlightPainter(Color.ORANGE) + def yellow = new DefaultHighlightPainter(Color.YELLOW) + def red = new DefaultHighlightPainter(Color.RED) + + // react to user actions + public void actionPerformed(ActionEvent event) { + if (event.actionCommand == '<<-') {scanIndex = Math.max(scanIndex - 1, 0)} + if (event.actionCommand == '->>') {scanIndex++} + doHighlights() + } + public void keyReleased(KeyEvent event) { + scanIndex = 0 + doHighlights() + } + + private resetView() { + swing.regexPane.highlighter.removeAllHighlights() + swing.targetPane.highlighter.removeAllHighlights() + swing.regexStatus.text = ' ' + swing.targetStatus.text = ' ' + } + + // the main regex logic + private doHighlights() { + try { + resetView() + // note: get the text from the underlying document, + // otherwise carriage return/line feeds different when using the JTextPane text + def regex = swing.regexPane.document.getText(0, swing.regexPane.document.length) + def target = swing.targetPane.document.getText(0, swing.targetPane.document.length) + + def matcher = (target =~ regex) + + // scan past the matches before the match we want + int scan = 0 + while (scan < scanIndex) { + matcher.find() + scan++ + } + if (matcher.find()) { + // highlight any captured groups + int i = 0 + while (i++ < matcher.groupCount()) { + swing.targetPane.highlighter.addHighlight(matcher.start(i), matcher.end(i), orange) + } + // highlight whole match + swing.targetPane.highlighter.addHighlight(matcher.start(), matcher.end(), yellow) + if (regex.length() != 0) { + swing.targetStatus.text = "Match #${scanIndex + 1} from ${matcher.start()} to ${matcher.end()}." + } + } else {// not found + scanIndex = Math.max(scan - 1, 0) + if (scanIndex > 0) {doHighlights()} + swing.targetStatus.text = "No match." + } + } catch (PatternSyntaxException e) { + swing.regexPane.highlighter.addHighlight(e.index, e.index + 2, red) + swing.regexStatus.text = e.description + } + } +} http://git-wip-us.apache.org/repos/asf/groovy-examples/blob/3bd1e181/src/main/groovy/swing/RegexCoachView.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/swing/RegexCoachView.groovy b/src/main/groovy/swing/RegexCoachView.groovy new file mode 100644 index 0000000..37868ed --- /dev/null +++ b/src/main/groovy/swing/RegexCoachView.groovy @@ -0,0 +1,55 @@ +/* + * 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 swing + +import static java.awt.BorderLayout.* +import static javax.swing.JSplitPane.VERTICAL_SPLIT +import static javax.swing.WindowConstants.EXIT_ON_CLOSE + +frame(title: 'The Groovy Regex Coach', location: [20, 40], size: [600, 500], defaultCloseOperation: EXIT_ON_CLOSE) { + panel { + borderLayout() + splitPane(orientation: VERTICAL_SPLIT, dividerLocation: 150) { + panel { + borderLayout() + label(constraints: NORTH, text: 'Regular expression:') + scrollPane(constraints: CENTER) { + textPane(id: 'regexPane') + } + label(constraints: SOUTH, id: 'regexStatus', text: ' ') + } + panel { + borderLayout() + label(constraints: NORTH, text: 'Target string:') + scrollPane(constraints: CENTER) { + textPane(id: 'targetPane') + } + panel(constraints: SOUTH) { + borderLayout() + label(constraints: NORTH, id: 'targetStatus', text: ' ') + panel(constraints: SOUTH) { + flowLayout() + button('<<-', id: 'scanLeft') + button('->>', id: 'scanRight') + } + } + } + } + } +} \ No newline at end of file
