Merge pull request #15 from oyarzun/checkstyle-cleanup

Cleanup checkstyle errors
diff --git a/pom.xml b/pom.xml
index 3773b0f..d3199a4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -134,7 +134,7 @@
                         <configuration>
                             <signature>
                                 <groupId>org.codehaus.mojo.signature</groupId>
-                                <artifactId>java17</artifactId>
+                                <artifactId>java18</artifactId>
                                 <version>1.0</version>
                             </signature>
                         </configuration>
@@ -280,6 +280,7 @@
                     <instrumentation>
                         <excludes>
                             <exclude>**/HelpMojo.class</exclude>
+                            <exclude>**/BuildMacMojo.class</exclude>
                         </excludes>
                     </instrumentation>
                 </configuration>
diff --git a/src/it/full/application/pom.xml b/src/it/full/application/pom.xml
index 4a0b28d..4fa3814 100644
--- a/src/it/full/application/pom.xml
+++ b/src/it/full/application/pom.xml
@@ -104,6 +104,7 @@
                                     <goal>autoupdate</goal>
                                     <goal>webstart-app</goal>
                                     <goal>build-installers</goal>
+                                    <goal>build-mac</goal>
                                 </goals>
                             </execution>
                         </executions>
diff --git a/src/main/java/org/apache/netbeans/nbm/BuildMacMojo.java b/src/main/java/org/apache/netbeans/nbm/BuildMacMojo.java
new file mode 100644
index 0000000..1070f51
--- /dev/null
+++ b/src/main/java/org/apache/netbeans/nbm/BuildMacMojo.java
@@ -0,0 +1,285 @@
+package org.apache.netbeans.nbm;
+
+/*
+ * 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.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.archiver.util.DefaultFileSet;
+import org.codehaus.plexus.archiver.zip.ZipArchiver;
+import org.codehaus.plexus.util.FileUtils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Builds a macOS application bundle for Mavenized NetBeans application. <br>
+ * Creates the brandingToken.app macOS Application bundle.
+ *
+ * @author <a href="mailto:oyarzun@apache.org">Christian Oyarzun</a>
+ */
+@Mojo( name = "build-mac",
+      requiresProject = true,
+      requiresDependencyResolution = ResolutionScope.RUNTIME,
+      threadSafe = true,
+      defaultPhase = LifecyclePhase.PACKAGE )
+public class BuildMacMojo
+        extends AbstractNbmMojo
+{
+
+    /**
+     * output directory.
+     */
+    @Parameter( defaultValue = "${project.build.directory}", required = true )
+    protected File outputDirectory;
+    /**
+     * The branding token for the application based on NetBeans platform.
+     */
+    @Parameter( property = "netbeans.branding.token", required = true )
+    protected String brandingToken;
+    /**
+     * Optional macOS icon file (in ICNS format) to use for the application bundle to replace the default icon 
+     * from the harness.
+     */
+    @Parameter( property = "netbeans.mac.icon", required = false )
+    private File macIconFile;
+    /**
+     * Optional macOS Info.plist file to use for the application bundle to replace the one from the harness.
+     * <p>
+     * ${app.title} is replaced by macAppTitle
+     * ${app.name} is replaced by brandingToken
+     * ${app.version} is replaced by project.version
+     */
+    @Parameter( property = "netbeans.mac.info.plist", required = false )
+    private File macInfoplistFile;
+    /**
+     * Optional macOS native launcher to use for the application bundle to replace the one from the harness.
+     */
+    @Parameter( property = "netbeans.mac.launcher", required = false )
+    private File macLauncherFile;
+    /**
+     * Zip the macOS app bundle to brandingToken-macOS.zip
+     */
+    @Parameter( property = "netbeans.mac.zipbundle", defaultValue = "false" )
+    private boolean macZipBundle;
+    /**
+     * Optional application title to use to replace ${app.title} for value of CFBundleName in the Info.plist file,
+     * otherwise brandingTokin is used.
+     */
+    @Parameter( property = "netbeans.mac.title", required = false )
+    private String macAppTitle;
+    /**
+     * The Maven Project.
+     */
+    @Parameter( required = true, readonly = true, property = "project" )
+    private MavenProject project;
+
+    // </editor-fold>
+    @Override
+    public void execute(  )
+            throws MojoExecutionException, MojoFailureException
+    {
+        if ( !"nbm-application".equals( project.getPackaging(  ) ) )
+        {
+            throw new MojoExecutionException(
+                    "This goal only makes sense on project with 'nbm-application' packaging." );
+        }
+
+        try
+        {
+            File bundleDir = new File( outputDirectory, brandingToken + ".app" );
+            bundleDir.mkdirs(  );
+
+            File contentsDir = new File( bundleDir, "Contents" );
+            contentsDir.mkdirs(  );
+
+            File resourcesDir = new File( contentsDir, "Resources" );
+            resourcesDir.mkdirs(  );
+
+            File appDir = new File( resourcesDir, brandingToken );
+            appDir.mkdirs(  );
+
+            File macOSDir = new File( contentsDir, "MacOS" );
+            macOSDir.mkdirs(  );
+
+            File app = new File( outputDirectory, brandingToken );
+            FileUtils.copyDirectoryStructure( app, appDir );
+
+            // delete windows executables
+            Files.delete( appDir.toPath(  ).resolve( "bin/" + brandingToken + ".exe" ) );
+            Files.delete( appDir.toPath(  ).resolve( "bin/" + brandingToken + "64.exe" ) );
+
+            copyIcon( resourcesDir );
+
+            copyInfoPlist( contentsDir );
+
+            copyLauncher( macOSDir );
+
+            if ( macZipBundle )
+            {
+                DefaultFileSet fileset = new DefaultFileSet( outputDirectory );
+                fileset.setIncludes( new String[] {bundleDir.getName(  ), bundleDir.getName(  ) + "/**"} );
+
+                ZipArchiver archiver = new ZipArchiver(  );
+                archiver.addFileSet( fileset );
+
+                File zipFile = new File( outputDirectory, brandingToken + "-macOS.zip" );
+                archiver.setDestFile( zipFile );
+
+                archiver.createArchive(  );
+            }
+
+        }
+        catch ( Exception ex )
+        {
+            throw new MojoExecutionException( "Build macOS application bundle failed: " + ex, ex );
+        }
+    }
+
+    void copyInfoPlist( File contentsDir ) throws IOException, MojoExecutionException
+    {
+        Path infoplist = contentsDir.toPath(  ).resolve( "Info.plist" );
+        if ( macAppTitle == null )
+        {
+            macAppTitle = brandingToken;
+        }
+
+        if ( macInfoplistFile != null )
+        {
+            try ( Stream<String> lines = Files.lines( macInfoplistFile.toPath(  ) ) ) 
+            {
+                String infoPListString = lines.map( s -> s.replace( "${app.title}", macAppTitle ) )
+                                              .map( s -> s.replace( "${app.name}", brandingToken ) )
+                                              .map( s -> s.replace( "${app.version}", project.getVersion(  ) ) )
+                                              .collect( Collectors.joining( "\n" ) );
+
+                Files.write( infoplist, infoPListString.getBytes(  ) );
+            }
+        }
+        else
+        {
+            URL harnessResourse = getClass(  ).getClassLoader(  ).getResource( "harness" );
+            JarURLConnection jarConnection = ( JarURLConnection ) harnessResourse.openConnection(  );
+            JarFile jarFile = jarConnection.getJarFile(  );
+
+            JarEntry entry = jarFile.getJarEntry( "harness/etc/Info.plist" );
+
+            if ( entry == null )
+            {
+                throw new MojoExecutionException( "macOS Info.plist not found in harness"
+                                                  + " or via macInfoplistFile parameter" );
+            }
+
+            try (BufferedReader reader =
+                    new BufferedReader( new InputStreamReader( jarFile.getInputStream( entry ) ) ) )
+            {
+                String infoPListString = reader.lines(  )
+                                               .map( s -> s.replace( "${app.title}", macAppTitle ) )
+                                               .map( s -> s.replace( "${app.name}", brandingToken ) )
+                                               .map( s -> s.replace( "${app.version}", project.getVersion(  ) ) )
+                                               .collect( Collectors.joining( "\n" ) );
+
+                Files.write( infoplist, infoPListString.getBytes(  ) );
+            }
+
+        }
+
+    }
+
+    void copyIcon( File resourcesDir ) throws IOException, MojoExecutionException
+    {
+        Path icnsPath = resourcesDir.toPath(  ).resolve( brandingToken + ".icns" );
+
+        if ( macIconFile != null )
+        {
+            FileUtils.copyFile( macIconFile, icnsPath.toFile(  ) );
+        }
+        else
+        {
+            URL harnessResourse = getClass(  ).getClassLoader(  ).getResource( "harness" );
+            JarURLConnection jarConnection = ( JarURLConnection ) harnessResourse.openConnection(  );
+            JarFile jarFile = jarConnection.getJarFile(  );
+
+            JarEntry entry = jarFile.getJarEntry( "harness/etc/applicationIcon.icns" );
+
+            if ( entry == null )
+            {
+                throw new MojoExecutionException( "macOS icon not found in harness or via macIconFile parameter" );
+            }
+
+            try ( InputStream entryInputStream = jarFile.getInputStream( entry ) )
+            {
+                Files.copy( entryInputStream, icnsPath, StandardCopyOption.REPLACE_EXISTING );
+            }
+
+            getLog(  ).info( "macOS icon not provided with macIconFile, using default icon." );
+        }
+    }
+
+    void copyLauncher( File macOSDir ) throws IOException, MojoExecutionException
+    {
+        Path launcherPath = macOSDir.toPath(  ).resolve( brandingToken );
+
+        if ( macLauncherFile != null )
+        {
+            FileUtils.copyFile( macLauncherFile, launcherPath.toFile(  ) );
+        }
+        else
+        {
+            URL harnessResourse = getClass(  ).getClassLoader(  ).getResource( "harness" );
+            JarURLConnection jarConnection = ( JarURLConnection ) harnessResourse.openConnection(  );
+            JarFile jarFile = jarConnection.getJarFile(  );
+
+            JarEntry entry = jarFile.getJarEntry( "harness/launchers/app-macOS" );
+
+            if ( entry == null )
+            {
+                throw new MojoExecutionException( "macOS launcher not found in harness"
+                                                 + " or via macLauncherFile parameter" );
+
+            }
+
+            try ( InputStream entryInputStream = jarFile.getInputStream( entry ) )
+            {
+                Files.copy( entryInputStream, launcherPath, StandardCopyOption.REPLACE_EXISTING );
+            }
+
+        }
+
+        launcherPath.toFile(  ).setExecutable( true );
+    }
+
+}