Author: rec
Date: Wed Jul  3 09:35:05 2013
New Revision: 1499292

URL: http://svn.apache.org/r1499292
Log:
[UIMA-2958] jcasgen Maven plugin: add option to exclude generating for imported 
types
- Stage full test projects into target folder
- Added option "limitToProject" (default: false)
- Formatting

Modified:
    
uima/uimaj/trunk/jcasgen-maven-plugin/src/main/java/org/apache/uima/tools/jcasgen/maven/JCasGenMojo.java
    
uima/uimaj/trunk/jcasgen-maven-plugin/src/test/java/org/apache/uima/tools/jcasgen/maven/JCasGenMojoTest.java

Modified: 
uima/uimaj/trunk/jcasgen-maven-plugin/src/main/java/org/apache/uima/tools/jcasgen/maven/JCasGenMojo.java
URL: 
http://svn.apache.org/viewvc/uima/uimaj/trunk/jcasgen-maven-plugin/src/main/java/org/apache/uima/tools/jcasgen/maven/JCasGenMojo.java?rev=1499292&r1=1499291&r2=1499292&view=diff
==============================================================================
--- 
uima/uimaj/trunk/jcasgen-maven-plugin/src/main/java/org/apache/uima/tools/jcasgen/maven/JCasGenMojo.java
 (original)
+++ 
uima/uimaj/trunk/jcasgen-maven-plugin/src/main/java/org/apache/uima/tools/jcasgen/maven/JCasGenMojo.java
 Wed Jul  3 09:35:05 2013
@@ -18,6 +18,8 @@
  */
 package org.apache.uima.tools.jcasgen.maven;
 
+import static java.util.Arrays.asList;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -66,293 +68,273 @@ import org.xml.sax.SAXException;
  * generate-resources phase runs before resources are copied.
  */
 @Mojo(name = "generate", defaultPhase = LifecyclePhase.PROCESS_RESOURCES, 
requiresDependencyResolution = ResolutionScope.COMPILE)
-public class JCasGenMojo
-    extends AbstractMojo
-{
-    @Component
-    private MavenProject project;
-
-    @Component
-    private BuildContext buildContext;
-
-    /**
-     * Type system descriptors to be included in JCas generation.
-     */
-    @Parameter(required = true)
-    private String[] typeSystemIncludes;
-
-    /**
-     * Type system descriptors to be excluded in JCas generation.
-     */
-    @Parameter(required = false)
-    private String[] typeSystemExcludes;
-
-    /**
-     * The directory where the generated sources will be written.
-     */
-    @Parameter(defaultValue = 
"${project.build.directory}/generated-sources/jcasgen", required = true)
-    private File outputDirectory;
-
-    public void execute()
-        throws MojoExecutionException, MojoFailureException
-    {
-        // add the generated sources to the build
-        if (!this.outputDirectory.exists()) {
-            this.outputDirectory.mkdirs();
-            this.buildContext.refresh(this.outputDirectory);
-        }
-        this.project.addCompileSourceRoot(this.outputDirectory.getPath());
+public class JCasGenMojo extends AbstractMojo {
+  @Component
+  private MavenProject project;
+
+  @Component
+  private BuildContext buildContext;
+
+  /**
+   * Type system descriptors to be included in JCas generation.
+   */
+  @Parameter(required = true)
+  private String[] typeSystemIncludes;
+
+  /**
+   * Type system descriptors to be excluded in JCas generation.
+   */
+  @Parameter(required = false)
+  private String[] typeSystemExcludes;
+
+  /**
+   * The directory where the generated sources will be written.
+   */
+  @Parameter(defaultValue = 
"${project.build.directory}/generated-sources/jcasgen", required = true)
+  private File outputDirectory;
+
+  /**
+   * Generate JCas wrappers only for current project.
+   */
+  @Parameter(defaultValue = "false", required = true)
+  private boolean limitToProject;
+
+  public void execute() throws MojoExecutionException, MojoFailureException {
+    // add the generated sources to the build
+    if (!this.outputDirectory.exists()) {
+      this.outputDirectory.mkdirs();
+      this.buildContext.refresh(this.outputDirectory);
+    }
+    this.project.addCompileSourceRoot(this.outputDirectory.getPath());
 
-        // assemble the classpath
-        List<String> elements;
-        try {
-            elements = this.project.getCompileClasspathElements();
-        }
-        catch (DependencyResolutionRequiredException e) {
-            throw new MojoExecutionException(e.getMessage(), e);
-        }
-        StringBuilder classpathBuilder = new StringBuilder();
-        for (String element : elements) {
-            if (classpathBuilder.length() > 0) {
-                classpathBuilder.append(File.pathSeparatorChar);
-            }
-            classpathBuilder.append(element);
-        }
-        String classpath = classpathBuilder.toString();
+    // assemble the classpath
+    List<String> elements;
+    try {
+      elements = this.project.getCompileClasspathElements();
+    } catch (DependencyResolutionRequiredException e) {
+      throw new MojoExecutionException(e.getMessage(), e);
+    }
+    
+    StringBuilder classpathBuilder = new StringBuilder();
+    for (String element : elements) {
+      if (classpathBuilder.length() > 0) {
+        classpathBuilder.append(File.pathSeparatorChar);
+      }
+      classpathBuilder.append(element);
+      getLog().debug("JCasGen: Adding to classpath '" + element + "'");
+    }
+    String classpath = classpathBuilder.toString();
 
-        // Locate the files to include
-        DirectoryScanner ds = new DirectoryScanner();
-        ds.setIncludes(typeSystemIncludes);
-        ds.setExcludes(typeSystemExcludes);
-        ds.setBasedir(this.project.getBasedir());
-        ds.setCaseSensitive(true);
-        ds.scan();
-
-        // Create a merged type system and check if any of the files has a 
delta
-        TypeSystemDescription typeSystem = new TypeSystemDescription_impl();
-        List<Import> imports = new ArrayList<Import>();
-        boolean contextDelta = false;
-        for (String descriptorLocation : ds.getIncludedFiles()) {
-            Import imp = new Import_impl();
-            // setLocation takes a string which must be a URL 
https://issues.apache.org/jira/browse/UIMA-2983 
-            URL url;
-            try {
-              url = (new File(ds.getBasedir(), 
descriptorLocation)).toURI().toURL();
-            } catch (MalformedURLException e) {
-              throw new RuntimeException(e);  // this should never happen for 
files
-            }
-            imp.setLocation(url.toString());
-            imports.add(imp);
-            
-            contextDelta |= this.buildContext
-                    .hasDelta(new File(ds.getBasedir(), descriptorLocation));
-        }
-        Import[] importArray = new Import[imports.size()];
-        typeSystem.setImports(imports.toArray(importArray));
+    // Locate the files to include
+    DirectoryScanner ds = new DirectoryScanner();
+    ds.setIncludes(typeSystemIncludes);
+    ds.setExcludes(typeSystemExcludes);
+    ds.setBasedir(project.getBasedir());
+    ds.setCaseSensitive(true);
+    getLog().debug("JCasGen: Scanning for descriptors in '" + ds.getBasedir() 
+ "'");
+    ds.scan();
+
+    // Create a merged type system and check if any of the files has a delta
+    TypeSystemDescription typeSystem = new TypeSystemDescription_impl();
+    List<Import> imports = new ArrayList<Import>();
+    boolean contextDelta = false;
+    for (String descriptorLocation : ds.getIncludedFiles()) {
+      File descriptorFile = new File(ds.getBasedir(), descriptorLocation);
+      this.getLog().info("JCasGen: Found descriptor '" + 
descriptorFile.getAbsolutePath() + "'");
+      Import imp = new Import_impl();
+      // setLocation takes a string which must be a URL
+      // https://issues.apache.org/jira/browse/UIMA-2983
+      URL url;
+      try {
+        url = descriptorFile.toURI().toURL();
+      } catch (MalformedURLException e) {
+        throw new RuntimeException(e); // this should never happen for files
+      }
+      imp.setLocation(url.toString());
+      imports.add(imp);
 
-        // Save type system to a file so we can pass it to the Jg
-        // Do this before resolving the imports
-        File typeSystemFile = null;
-        try {
-            OutputStream typeSystemOs = null;
-            try {
-                typeSystemFile = 
File.createTempFile("jcasgen-aggregate-types", ".xml");
-                typeSystemOs = new FileOutputStream(typeSystemFile);
-                typeSystem.toXML(typeSystemOs);
-            }
-            catch (IOException e) {
-                throw new MojoExecutionException(e.getMessage(), e.getCause());
-            }
-            catch (SAXException e) {
-                throw new MojoExecutionException(e.getMessage(), e.getCause());
-            }
-            finally {
-                IOUtil.close(typeSystemOs);
-            }
-    
-            // skip JCasGen if there are no changes in the type system file or 
the files it
-            // references
-            // hasDelta resolves the imports!
-            if (!contextDelta && !this.hasDelta(typeSystem, classpath)) {
-                this.getLog().info("JCasGen: Skipped, since no type system 
changes were detected");
-                return;
-            }
-            
-            // run JCasGen to generate the Java sources
-            Jg jCasGen = new Jg();
-            String[] args = new String[] { "-jcasgeninput", 
typeSystemFile.getAbsolutePath(),
-                    "-jcasgenoutput", this.outputDirectory.getAbsolutePath(), 
"=jcasgenclasspath",
-                    classpath };
-            try {
-                jCasGen.main0(args, null, new JCasGenProgressMonitor(), new 
JCasGenErrors());
-            }
-            catch (JCasGenException e) {
-                throw new MojoExecutionException(e.getMessage(), e.getCause());
-            }
-        }
-        finally {
-            if (typeSystemFile != null) {
-                typeSystemFile.delete();
-            }
-        }
+      contextDelta |= this.buildContext.hasDelta(new File(ds.getBasedir(), 
descriptorLocation));
+    }
+    Import[] importArray = new Import[imports.size()];
+    typeSystem.setImports(imports.toArray(importArray));
 
-        // signal that the output directory has changed
-        this.buildContext.refresh(this.outputDirectory);
+    // Save type system to a file so we can pass it to the Jg
+    // Do this before resolving the imports
+    OutputStream typeSystemOs = null;
+    File typeSystemFile;
+    try {
+      typeSystemFile = new File(project.getBuild().getDirectory(), 
"jcasgen/typesystem.xml");
+      getLog().debug("JCasGen: Writing master descriptor to in '" + 
typeSystemFile + "'");
+      typeSystemFile.getParentFile().mkdirs();
+      typeSystemOs = new FileOutputStream(typeSystemFile);
+      typeSystem.toXML(typeSystemOs);
+    } catch (IOException e) {
+      throw new MojoExecutionException(e.getMessage(), e.getCause());
+    } catch (SAXException e) {
+      throw new MojoExecutionException(e.getMessage(), e.getCause());
+    } finally {
+      IOUtil.close(typeSystemOs);
     }
 
-    private class JCasGenProgressMonitor
-        implements IProgressMonitor
-    {
+    // skip JCasGen if there are no changes in the type system file or the 
files it
+    // references hasDelta resolves the imports!
+    if (!contextDelta && !this.hasDelta(typeSystem, classpath)) {
+      this.getLog().info("JCasGen: Skipped, since no type system changes were 
detected");
+      return;
+    }
 
-        public JCasGenProgressMonitor()
-        {
-        }
+    List<String> args = new ArrayList<String>();
+    if (limitToProject) {
+      File limitToDirectory = project.getBasedir().getAbsoluteFile();
+      getLog().info("JCasGen: Limiting generation to descriptors in '" + 
limitToDirectory + "'");
+      args.addAll(asList("-limitToDirectory", limitToDirectory.toString()));
+    }
+    args.addAll(asList("-jcasgeninput", typeSystemFile.getAbsolutePath(), 
"-jcasgenoutput",
+            this.outputDirectory.getAbsolutePath(), "=jcasgenclasspath", 
classpath));
 
-        public void done()
-        {
-        }
+    // run JCasGen to generate the Java sources
+    Jg jCasGen = new Jg();
+    try {
+      jCasGen.main0(args.toArray(new String[args.size()]), null, new 
JCasGenProgressMonitor(),
+              new JCasGenErrors());
+    } catch (JCasGenException e) {
+      throw new MojoExecutionException(e.getMessage(), e.getCause());
+    }
 
-        public void beginTask(String name, int totalWorked)
-        {
-        }
+    // signal that the output directory has changed
+    this.buildContext.refresh(this.outputDirectory);
+  }
 
-        public void subTask(String message)
-        {
-            getLog().info("JCasGen: " + message);
-        }
+  private class JCasGenProgressMonitor implements IProgressMonitor {
 
-        public void worked(int work)
-        {
-        }
+    public JCasGenProgressMonitor() {
     }
 
-    private class JCasGenErrors
-        implements IError
-    {
+    public void done() {
+    }
 
-        public JCasGenErrors()
-        {
-        }
+    public void beginTask(String name, int totalWorked) {
+    }
 
-        public void newError(int severity, String message, Exception exception)
-        {
-            String fullMessage = "JCasGen: " + message;
-            if (severity == IError.INFO) {
-                getLog().info(fullMessage, exception);
-            }
-            else if (severity == IError.WARN) {
-                getLog().warn(fullMessage, exception);
-            }
-            else if (severity == IError.ERROR) {
-                throw new JCasGenException(exception.getMessage(), exception);
-            }
-            else {
-                throw new UnsupportedOperationException("Unknown severity 
level: " + severity);
-            }
-        }
+    public void subTask(String message) {
+      getLog().info("JCasGen: " + message);
     }
 
-    private static class JCasGenException
-        extends RuntimeException
-    {
-        private static final long serialVersionUID = 1L;
-
-        public JCasGenException(String message, Throwable cause)
-        {
-            super(message, cause);
-        }
+    public void worked(int work) {
     }
+  }
 
-    private boolean hasDelta(TypeSystemDescription typeSystemDescription, 
String classpath)
-    {
-        // load the type system and resolve the imports using the classpath
-//        TypeSystemDescription typeSystemDescription;
-        try {
-//            XMLInputSource in = new 
XMLInputSource(typeSystemFile.toURI().toURL());
-//            typeSystemDescription = 
UIMAFramework.getXMLParser().parseTypeSystemDescription(in);
-            ResourceManager resourceManager = 
UIMAFramework.newDefaultResourceManager();
-            resourceManager.setExtensionClassPath(classpath, true);
-            typeSystemDescription.resolveImports(resourceManager);
-            // on any exception, the type system was invalid, so assume no 
files have changed
-        }
-        catch (InvalidXMLException e) {
-            return false;
-        }
-        catch (MalformedURLException e) {
-            return false;
-        }
-//        catch (IOException e) {
-//            return false;
-//        }
-
-        File buildOutputDirectory = new 
File(this.project.getBuild().getOutputDirectory());
-
-        // map each resource from its target location to its source location
-        Map<File, File> targetToSource = new HashMap<File, File>();
-        for (Resource resource : this.project.getResources()) {
-            File resourceDir = new File(resource.getDirectory());
-            if (resourceDir.exists()) {
-
-                // scan for the resource files
-                List<String> includes = resource.getIncludes();
-                if (includes.isEmpty()) {
-                    includes = Arrays.asList("**");
-                }
-                List<String> excludes = resource.getExcludes();
-                DirectoryScanner scanner = new DirectoryScanner();
-                scanner.setBasedir(resourceDir);
-                scanner.setIncludes(includes.toArray(new 
String[includes.size()]));
-                scanner.setExcludes(excludes.toArray(new 
String[excludes.size()]));
-                scanner.scan();
-
-                // map each of the resources from its target location to its 
source location
-                String targetPath = resource.getTargetPath();
-                for (String filePath : scanner.getIncludedFiles()) {
-                    File sourceFile = new File(resourceDir, filePath);
-                    File baseDirectory = targetPath != null ? new 
File(buildOutputDirectory,
-                            targetPath) : buildOutputDirectory;
-                    File targetFile = new File(baseDirectory, filePath);
-                    targetToSource.put(targetFile, sourceFile);
-                }
-            }
+  private class JCasGenErrors implements IError {
+
+    public JCasGenErrors() {
+    }
+
+    public void newError(int severity, String message, Exception exception) {
+      String fullMessage = "JCasGen: " + message;
+      if (severity == IError.INFO) {
+        getLog().info(fullMessage, exception);
+      } else if (severity == IError.WARN) {
+        getLog().warn(fullMessage, exception);
+      } else if (severity == IError.ERROR) {
+        throw new JCasGenException(exception.getMessage(), exception);
+      } else {
+        throw new UnsupportedOperationException("Unknown severity level: " + 
severity);
+      }
+    }
+  }
+
+  private static class JCasGenException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    public JCasGenException(String message, Throwable cause) {
+      super(message, cause);
+    }
+  }
+
+  private boolean hasDelta(TypeSystemDescription typeSystemDescription, String 
classpath) {
+    // load the type system and resolve the imports using the classpath
+    // TypeSystemDescription typeSystemDescription;
+    try {
+      // XMLInputSource in = new 
XMLInputSource(typeSystemFile.toURI().toURL());
+      // typeSystemDescription = 
UIMAFramework.getXMLParser().parseTypeSystemDescription(in);
+      ResourceManager resourceManager = 
UIMAFramework.newDefaultResourceManager();
+      resourceManager.setExtensionClassPath(classpath, true);
+      typeSystemDescription.resolveImports(resourceManager);
+      // on any exception, the type system was invalid, so assume no files 
have changed
+    } catch (InvalidXMLException e) {
+      return false;
+    } catch (MalformedURLException e) {
+      return false;
+    }
+    // catch (IOException e) {
+    // return false;
+    // }
+
+    File buildOutputDirectory = new 
File(this.project.getBuild().getOutputDirectory());
+
+    // map each resource from its target location to its source location
+    Map<File, File> targetToSource = new HashMap<File, File>();
+    for (Resource resource : this.project.getResources()) {
+      File resourceDir = new File(resource.getDirectory());
+      if (resourceDir.exists()) {
+
+        // scan for the resource files
+        List<String> includes = resource.getIncludes();
+        if (includes.isEmpty()) {
+          includes = Arrays.asList("**");
+        }
+        List<String> excludes = resource.getExcludes();
+        DirectoryScanner scanner = new DirectoryScanner();
+        scanner.setBasedir(resourceDir);
+        scanner.setIncludes(includes.toArray(new String[includes.size()]));
+        scanner.setExcludes(excludes.toArray(new String[excludes.size()]));
+        scanner.scan();
+
+        // map each of the resources from its target location to its source 
location
+        String targetPath = resource.getTargetPath();
+        for (String filePath : scanner.getIncludedFiles()) {
+          File sourceFile = new File(resourceDir, filePath);
+          File baseDirectory = targetPath != null ? new 
File(buildOutputDirectory, targetPath)
+                  : buildOutputDirectory;
+          File targetFile = new File(baseDirectory, filePath);
+          targetToSource.put(targetFile, sourceFile);
         }
+      }
+    }
 
-        // search through the type system description for source files that 
have changed
-        for (TypeDescription type : typeSystemDescription.getTypes()) {
-            URL typeSystemURL = type.getSourceUrl();
-            if (typeSystemURL != null) {
-                File targetFile;
-                try {
-                    targetFile = new File(typeSystemURL.toURI());
-                    // for any type system source that is not a File, assume 
it has not changed
-                }
-                catch (URISyntaxException e) {
-                    continue;
-                }
-                catch (IllegalArgumentException e) {
-                    continue;
-                }
-                File sourceFile = targetToSource.get(targetFile);
-                if (sourceFile != null) {
-                    // for any type system file that is also a resource file, 
return true if it has
-                    // changed
-                    if (this.buildContext.hasDelta(sourceFile)) {
-                        this.getLog().info("Type system file " + sourceFile + 
" has changed");
-                        return true;
-                    }
-                    // for any type system file that is in the same project, 
return true if it has
-                    // changed
-                    if (targetFile.getAbsolutePath().startsWith(
-                            this.project.getBasedir().getAbsolutePath())) {
-                        if (this.buildContext.hasDelta(targetFile)) {
-                            this.getLog().info("Type system file " + 
sourceFile + " has changed");
-                            return true;
-                        }
-                    }
-                }
+    // search through the type system description for source files that have 
changed
+    for (TypeDescription type : typeSystemDescription.getTypes()) {
+      URL typeSystemURL = type.getSourceUrl();
+      if (typeSystemURL != null) {
+        File targetFile;
+        try {
+          targetFile = new File(typeSystemURL.toURI());
+          // for any type system source that is not a File, assume it has not 
changed
+        } catch (URISyntaxException e) {
+          continue;
+        } catch (IllegalArgumentException e) {
+          continue;
+        }
+        File sourceFile = targetToSource.get(targetFile);
+        if (sourceFile != null) {
+          // for any type system file that is also a resource file, return 
true if it has
+          // changed
+          if (this.buildContext.hasDelta(sourceFile)) {
+            this.getLog().info("Type system file " + sourceFile + " has 
changed");
+            return true;
+          }
+          // for any type system file that is in the same project, return true 
if it has
+          // changed
+          if 
(targetFile.getAbsolutePath().startsWith(this.project.getBasedir().getAbsolutePath()))
 {
+            if (this.buildContext.hasDelta(targetFile)) {
+              this.getLog().info("Type system file " + sourceFile + " has 
changed");
+              return true;
             }
+          }
         }
-        // no type system files have changed
-        return false;
+      }
     }
+    // no type system files have changed
+    return false;
+  }
 }

Modified: 
uima/uimaj/trunk/jcasgen-maven-plugin/src/test/java/org/apache/uima/tools/jcasgen/maven/JCasGenMojoTest.java
URL: 
http://svn.apache.org/viewvc/uima/uimaj/trunk/jcasgen-maven-plugin/src/test/java/org/apache/uima/tools/jcasgen/maven/JCasGenMojoTest.java?rev=1499292&r1=1499291&r2=1499292&view=diff
==============================================================================
--- 
uima/uimaj/trunk/jcasgen-maven-plugin/src/test/java/org/apache/uima/tools/jcasgen/maven/JCasGenMojoTest.java
 (original)
+++ 
uima/uimaj/trunk/jcasgen-maven-plugin/src/test/java/org/apache/uima/tools/jcasgen/maven/JCasGenMojoTest.java
 Wed Jul  3 09:35:05 2013
@@ -26,7 +26,6 @@ import junit.framework.Assert;
 
 import org.apache.maven.execution.DefaultMavenExecutionRequest;
 import org.apache.maven.execution.MavenExecutionRequest;
-import org.apache.maven.model.Build;
 import org.apache.maven.plugin.testing.AbstractMojoTestCase;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.ProjectBuilder;
@@ -54,8 +53,12 @@ public class JCasGenMojoTest extends Abs
 
   public void test(String projectName, String... types) throws Exception {
 
-    File projectDirectory = getTestFile("src/test/resources/" + projectName);
-    File buildDirectory = getTestFile("target/project-" + projectName + 
"-test");
+    File projectSourceDirectory = getTestFile("src/test/resources/" + 
projectName);
+    File projectDirectory = getTestFile("target/project-" + projectName + 
"-test");
+
+    // Stage project to target folder
+    FileUtils.copyDirectoryStructure(projectSourceDirectory, projectDirectory);
+    
     File pomFile = new File(projectDirectory, "/pom.xml");
     assertNotNull(pomFile);
     assertTrue(pomFile.exists());
@@ -67,18 +70,12 @@ public class JCasGenMojoTest extends Abs
     MavenProject project = projectBuilder.build(pomFile, 
buildingRequest).getProject();
     assertNotNull(project);
 
-    // set the base directory (or it will write to src/test/resources/)
-    Build build = project.getModel().getBuild();
-    build.setDirectory(buildDirectory.getPath());
-    File outputDirectory = new File(buildDirectory, "target/classes");
-    build.setOutputDirectory(outputDirectory.getPath());
-
     // copy resources
     File source = new File(projectDirectory, "src/main/resources");
     if (source.exists()) {
-      FileUtils.copyDirectoryStructure(source, outputDirectory);
+      FileUtils.copyDirectoryStructure(source, new 
File(project.getBuild().getOutputDirectory()));
     }
-
+    
     // load the Mojo
     JCasGenMojo generate = (JCasGenMojo) this.lookupConfiguredMojo(project, 
"generate");
     assertNotNull(generate);
@@ -90,7 +87,7 @@ public class JCasGenMojoTest extends Abs
     generate.execute();
 
     // check that the Java files have been generated
-    File jCasGenDirectory = new File(buildDirectory, 
"generated-sources/jcasgen");
+    File jCasGenDirectory = new File(project.getBasedir(), 
"target/generated-sources/jcasgen");
     
     // Record all the files that were generated
     DirectoryScanner ds = new DirectoryScanner();


Reply via email to