/** * 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 /////////////// package org.apache.jena.tools.schemagen; // Imports /////////////// import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import jena.schemagen; import jena.schemagen.SchemagenOptions.OPT; import org.apache.jena.rdf.model.Resource ; import org.apache.jena.rdf.model.ResourceFactory ; import org.apache.maven.plugin.AbstractMojo; 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.codehaus.plexus.util.DirectoryScanner; /** * <p>Maven plugin to execute Jena schemagen as part of a Jena-based * project build cycle * </p> */ @Mojo(name="translate", defaultPhase=LifecyclePhase.GENERATE_SOURCES) public class SchemagenMojo extends AbstractMojo { /***********************************/ /* Constants */ /***********************************/ /** Default output location */ public static final String GENERATED_SOURCES = File.separator + "generated-sources"; /** Default pattern for includes */ /** Name of default options element */ public static final String DEFAULT_OPTIONS_ELEM = "default"; /***********************************/ /* Static variables */ /***********************************/ /***********************************/ /* Instance variables */ /***********************************/ /** * @parameter property="project.build.directory" */ @Parameter(property="project.build.directory") private String projectBuildDir; /** * Array of file patterns to include in processing */ @Parameter private String[] includes = new String[0]; /** * Array of file patterns to exclude from processing */ @Parameter private String[] excludes = new String[0]; /** * Options for individual files */ @Parameter private List<Source> fileOptions; /** * The current base directory of the project */ @Parameter(property="basedir") private File baseDir; /** The default options object, if any */ private SchemagenOptions defaultOptions; /** Map of source options, indexed by name */ private Map<String, SchemagenOptions> optIndex = new HashMap<>(); /***********************************/ /* Constructors */ /***********************************/ /***********************************/ /* External signature methods */ /***********************************/ @Override public void execute() throws MojoExecutionException, MojoFailureException { try { // set the default defaults defaultOptions = new SchemagenOptions(getDefaultOutputDir()); getLog().info( "Starting schemagen execute() ..."); // next process the various options specs if( fileOptions != null ){ for (Source s: fileOptions) { if (s.isDefaultOptions()) { handleDefaultOptions( s ); } else { handleOption( s ); } } } if( defaultOptions == null ){ handleDefaultOptions( new Source() ); } // then the files themselves for (String fileName: matchFileNames()) { processFile( fileName ); } } catch (SchemagenOptionsConfigurationException e) { throw new MojoExecutionException( "Error during default schemagen options creation", e); } } /** * Return a list of the file names to be processed by schemagen. These are * determined by processing the Ant style paths given in the <code>includes</code> * and <code>excludes</code> parameters. * * @return Non-null but possibly empty list of files to process, sorted into lexical order */ protected List<String> matchFileNames() { DirectoryScanner ds = new DirectoryScanner(); ds.setExcludes( excludes ); ds.setIncludes( includes ); ds.setBasedir( getBaseDir() ); ds.scan(); List<String> files = new ArrayList<>( Arrays.asList( ds.getIncludedFiles() ) ); Collections.sort( files ); //add http includes for( String include : includes ){ if( include.startsWith("http:") || include.startsWith("https:")){ files.add( include ); } } return files; } /** * Return the default options structure, or null * @return The default options */ protected SchemagenOptions getDefaultOptions() { return defaultOptions; } /** Return the value of <code>${project.build.directory}</code> */ public String getProjectBuildDir() { return projectBuildDir; } /** * Handle the default options by creating a default options object and assigning * the options values from the given source object. * @param defOptionsSource The source object containing the default options * @throws SchemagenOptionsConfigurationException */ protected void handleDefaultOptions( Source defOptionsSource ) throws SchemagenOptionsConfigurationException { SchemagenOptions defSo = new SchemagenOptions(getDefaultOutputDir(), defOptionsSource); if (defaultOptions != null) { defSo.setParent( defaultOptions ); } defaultOptions = defSo; } /** * Process the given options specification for one of the input files * by attaching the default options and indexing. * * @param optionSpec Specification of the options for a given file * @throws SchemagenOptionsConfigurationException */ protected void handleOption( Source optionSpec ) throws SchemagenOptionsConfigurationException { SchemagenOptions so = new SchemagenOptions(getDefaultOutputDir(), optionSpec); if (optionSpec.getInput() != null && !optionSpec.getInput().isEmpty()) { so.setParent( getDefaultOptions() ); optIndex.put( optionSpec.getInput(), so ); } else { getLog().info( "ignoring <source> element because the fileName is not specified" ); } } /** * Delegate the processing of the given file to schemagen itself * @param fileName * @throws SchemagenOptionsConfigurationException */ protected void processFile( String fileName ) throws MojoExecutionException, SchemagenOptionsConfigurationException { getLog().info( "processFile with " + fileName ); getLog().info( optIndex.keySet().toString() ); SchemagenOptions so = optIndex.get( fileName ); getLog().info( "so = " + so ); // if we have no options carrier for this file, we create one to contain // the name of the input file, and link it to the defaults String soFileName; if (so == null) { so = new SchemagenOptions(getDefaultOptions().getOutputOption()); soFileName = fileName; so.setParent( getDefaultOptions() ); } else { soFileName = so.getOption( OPT.INPUT ).asLiteral().getString(); } getLog().info( "input before adjustment: " + soFileName ); boolean relative = !(soFileName.startsWith( "http:" ) || soFileName.startsWith( "https:" ) || soFileName.startsWith( "file:" )); getLog().info( "relative = " + relative ); getLog().info( "baseDir = " + baseDir ); getLog().info( "getBaseDir() = " + getBaseDir() ); soFileName = relative ? "file:" + baseDir + File.separator + soFileName : soFileName; //fix windows paths if( File.separator.equals("\\") ){ soFileName = soFileName.replaceAll( "\\\\", "/" ); } getLog().info( "input after adjustment: " + soFileName ); Resource input = ResourceFactory.createResource( soFileName ); so.setOption( OPT.INPUT, input ); getLog().info( "about to call run(): " ); ensureTargetDirectory( so ); new SchemagenAdapter().run( so ); } /***********************************/ /* Internal implementation methods */ /***********************************/ public void setExcludes( String[] excludes ) { this.excludes = excludes; } public void setIncludes( String[] includes ) { this.includes = includes; } /** * Append the given string to the array of included file patterns * @param incl File pattern string to append to <code>this.includes</code> */ public void addIncludes( String incl ) { String[] incls = new String[this.includes.length + 1]; int i = 0; for (String s: this.includes) { incls[i++] = s; } incls[i] = incl; this.includes = incls; } /** * Append the given string to the array of excluded file patterns * @param excl File pattern string to append to <code>this.excludes</code> */ public void addExcludes( String excl ) { String[] excls = new String[this.excludes.length + 1]; int i = 0; for (String s: this.excludes) { excls[i++] = s; } excls[i] = excl; this.excludes = excls; } /** * Return the base directory for the plugin, which should be supplied * by plexus, but if not we default to the current working directory. * * @return The base directory as a file */ protected File getBaseDir() { return (baseDir == null) ? new File(".").getAbsoluteFile() : baseDir; } /** * Ensure that the output directory exists */ protected void ensureTargetDirectory( SchemagenOptions so ) throws MojoExecutionException { File gs = new File( so.getOutputOption() ); if (!gs.exists()) { gs.mkdirs(); } else if (!gs.isDirectory()) { getLog().error( "The output location is not a directory: " + gs.getPath() ); throw new MojoExecutionException( "Already exists as file: " + gs.getPath() ); } else if (!gs.canWrite()) { getLog().error( "Output directory exists but is not writable: " + gs.getPath() ); throw new MojoExecutionException( "Not writable: " + gs.getPath() ); } } protected String getDefaultOutputDir(){ return projectBuildDir + GENERATED_SOURCES; } /***********************************/ /* Inner classes */ /***********************************/ /** * Adapter class to invoke the schemagen tool with a given set of options */ protected class SchemagenAdapter extends schemagen { public void run( SchemagenOptions options ) { go( options ); } } }