From d474c87b5d658c7a481eb431c5fd6fb815f2e217 Mon Sep 17 00:00:00 2001 From: mmews Date: Wed, 21 Feb 2024 11:13:45 +0100 Subject: [PATCH] migrate migrate --- .../n4js/generator/AbstractSubGenerator.java | 536 ++++++++++++++++++ .../n4js/generator/AbstractSubGenerator.xtend | 482 ---------------- .../n4js/packagejson/PackageJsonBuilder.java | 380 +++++++++++++ .../n4js/packagejson/PackageJsonBuilder.xtend | 358 ------------ 4 files changed, 916 insertions(+), 840 deletions(-) create mode 100644 plugins/org.eclipse.n4js/src/org/eclipse/n4js/generator/AbstractSubGenerator.java delete mode 100644 plugins/org.eclipse.n4js/src/org/eclipse/n4js/generator/AbstractSubGenerator.xtend create mode 100644 plugins/org.eclipse.n4js/src/org/eclipse/n4js/packagejson/PackageJsonBuilder.java delete mode 100644 plugins/org.eclipse.n4js/src/org/eclipse/n4js/packagejson/PackageJsonBuilder.xtend diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/generator/AbstractSubGenerator.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/generator/AbstractSubGenerator.java new file mode 100644 index 0000000000..c7b33af9a4 --- /dev/null +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/generator/AbstractSubGenerator.java @@ -0,0 +1,536 @@ +/** + * Copyright (c) 2016 NumberFour AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * NumberFour AG - Initial API and implementation + */ +package org.eclipse.n4js.generator; + +import static org.eclipse.xtext.diagnostics.Severity.ERROR; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.filter; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.head; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.toList; + +import java.io.StringWriter; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import org.apache.log4j.Logger; +import org.eclipse.emf.common.EMFPlugin; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.n4js.N4JSGlobals; +import org.eclipse.n4js.N4JSLanguageConstants; +import org.eclipse.n4js.generator.IGeneratorMarkerSupport.Severity; +import org.eclipse.n4js.n4JS.Script; +import org.eclipse.n4js.packagejson.projectDescription.ProjectType; +import org.eclipse.n4js.resource.N4JSCache; +import org.eclipse.n4js.resource.N4JSResource; +import org.eclipse.n4js.ts.types.TModule; +import org.eclipse.n4js.utils.FolderContainmentHelper; +import org.eclipse.n4js.utils.ResourceNameComputer; +import org.eclipse.n4js.utils.ResourceType; +import org.eclipse.n4js.utils.StaticPolyfillHelper; +import org.eclipse.n4js.utils.URIUtils; +import org.eclipse.n4js.workspace.N4JSProjectConfigSnapshot; +import org.eclipse.n4js.workspace.N4JSSourceFolderSnapshot; +import org.eclipse.n4js.workspace.N4JSWorkspaceConfigSnapshot; +import org.eclipse.n4js.workspace.WorkspaceAccess; +import org.eclipse.xtend.lib.annotations.Accessors; +import org.eclipse.xtext.generator.AbstractFileSystemAccess; +import org.eclipse.xtext.generator.IFileSystemAccess; +import org.eclipse.xtext.generator.IFileSystemAccess2; +import org.eclipse.xtext.generator.IGenerator2; +import org.eclipse.xtext.generator.IGeneratorContext; +import org.eclipse.xtext.generator.OutputConfiguration; +import org.eclipse.xtext.nodemodel.util.NodeModelUtils; +import org.eclipse.xtext.service.OperationCanceledManager; +import org.eclipse.xtext.util.CancelIndicator; +import org.eclipse.xtext.util.UriExtensions; +import org.eclipse.xtext.validation.CheckMode; +import org.eclipse.xtext.validation.IResourceValidator; +import org.eclipse.xtext.validation.Issue; + +import com.google.inject.Inject; + +/** + * All sub generators should extend this class. It provides basic blocks of the logic, and shared implementations. + */ +abstract public class AbstractSubGenerator implements ISubGenerator, IGenerator2 { + private final static Logger LOGGER = Logger.getLogger(AbstractSubGenerator.class); + + @Accessors + private CompilerDescriptor compilerDescriptor = null; + + /***/ + @Inject + protected StaticPolyfillHelper staticPolyfillHelper; + + /***/ + @Inject + protected WorkspaceAccess workspaceAccess; + + /***/ + @Inject + protected ResourceNameComputer resourceNameComputer; + + /***/ + @Inject + protected IResourceValidator resVal; + + /***/ + @Inject + protected N4JSCache cache; + + /***/ + @Inject + protected IGeneratorMarkerSupport genMarkerSupport; + + /***/ + @Inject + protected OperationCanceledManager operationCanceledManager; + + /***/ + @Inject + protected GeneratorExceptionHandler exceptionHandler; + + /***/ + @Inject + protected N4JSPreferenceAccess preferenceAccess; + + @Inject + private FolderContainmentHelper containmentHelper; + + @Inject + private UriExtensions uriExtensions; + + @Override + public CompilerDescriptor getCompilerDescriptor() { + if (compilerDescriptor == null) { + compilerDescriptor = getDefaultDescriptor(); + } + return compilerDescriptor; + } + + @Override + public void doGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context) { + doGenerate(input, fsa); + } + + @Override + public void beforeGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context) { + // TODO Auto-generated method stub + + } + + @Override + public void afterGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context) { + // TODO Auto-generated method stub + + } + + /** + * This override declares an {@link IFileSystemAccess} parameter. At runtime, its actual type depends on whether IDE + * or headless, which in turn determines whether the actual argument has a progress monitor or not: + * + */ + @Override + public void doGenerate(Resource input, IFileSystemAccess fsa) { + if (justCopy(input)) { + copyWithoutTranspilation(input, fsa); + return; + } + + N4JSWorkspaceConfigSnapshot ws = workspaceAccess.getWorkspaceConfig(input); + + try { + // remove error-marker + genMarkerSupport.deleteMarker(input); + + updateOutputPath(ws, fsa, getCompilerID(), input); + internalDoGenerate(ws, input, GeneratorOption.DEFAULT_OPTIONS, fsa); + + } catch (UnresolvedProxyInSubGeneratorException e) { + genMarkerSupport.createMarker(input, e.getMessage(), Severity.ERROR); + + } catch (Exception e) { + // cancellation is not an error case, so simply propagate as usual + operationCanceledManager.propagateIfCancelException(e); + + // issue error marker + String target = input.getURI() == null ? "unknown" : input.getURI().toString(); + if (input instanceof N4JSResource && ((N4JSResource) input).getModule() != null) { + target = ((N4JSResource) input).getModule().getModuleSpecifier(); + } + String msgMarker = "Severe error occurred while transpiling module " + target + + ". Check error log for details about the failure."; + genMarkerSupport.createMarker(input, msgMarker, Severity.ERROR); + + // re-throw as GeneratorException to have the frameworks notify the error. + if (e instanceof GeneratorException) { + throw e; + } + String msg = (e.getMessage() == null) ? "type=" + e.getClass().getName() : "message=" + e.getMessage(); + exceptionHandler.handleError("Severe error occurred in transpiler=" + getCompilerID() + " " + msg + ".", e); + } + } + + @Override + public boolean shouldBeCompiled(Resource input, CancelIndicator monitor) { + N4JSWorkspaceConfigSnapshot ws = workspaceAccess.getWorkspaceConfig(input); + + boolean autobuildEnabled = isActive(input); + boolean isXPECTMode = N4JSGlobals.XT_FILE_EXTENSION + .equals(URIUtils.fileExtension(input.getURI()).toLowerCase()); + URI inputUri = input.getURI(); + + boolean result = (autobuildEnabled + && isGenerateProjectType(ws, inputUri) + && hasOutput(ws, inputUri) + && isOutputNotInSourceContainer(ws, inputUri) + && isOutsideOfOutputFolder(ws, inputUri) + && isSource(ws, inputUri) + && (isNoValidate(ws, inputUri) + || isExternal(ws, inputUri) + // if platform is running (but not in XPECT mode) the generator is called from the builder, + // hence cannot have any validation errors + // (note: XPECT swallows errors hence we cannot rely on Eclipse in case of .xt files) + || ((EMFPlugin.IS_ECLIPSE_RUNNING && !isXPECTMode) || hasNoErrors(input, monitor)))) + && (!isStaticPolyfillingModule(input)) // compile driven by filled type + && hasNoPolyfillErrors(input, monitor); + return result; + } + + private boolean hasOutput(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { + return getOutputPath(ws, n4jsSourceURI) != null; + } + + private boolean isSource(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { + return ws.findSourceFolderContaining(n4jsSourceURI) != null; + } + + private boolean isNoValidate(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { + return ws.isNoValidate(n4jsSourceURI); + } + + private boolean isExternal(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { + N4JSSourceFolderSnapshot sourceContainerOpt = ws.findSourceFolderContaining(n4jsSourceURI); + if (sourceContainerOpt != null) { + N4JSSourceFolderSnapshot sourceContainer = sourceContainerOpt; + return sourceContainer.isExternal(); + } + return false; + } + + /** + * If the resource has a static polyfill, then ensure it is error-free. Calls + * {@link #hasNoErrors(Resource, CancelIndicator)} on the static polyfill resource. + */ + private boolean hasNoPolyfillErrors(Resource input, CancelIndicator monitor) { + N4JSResource resSPoly = staticPolyfillHelper.getStaticPolyfillResource(input); + if (resSPoly == null) { + return true; + } + // re-validation is necessary since the changes of the current resource (i.e. filled resource) + // can affect the filling resource in a way that validation errors will be removed or created. + cache.recreateIssues(resVal, resSPoly, CheckMode.ALL, monitor); + return hasNoErrors(resSPoly, monitor); + } + + /** + * Does validation report no errors for the given resource? If errors exists, log them as a side-effect. If + * validation was canceled before finishing, don't assume absence of errors. + */ + private boolean hasNoErrors(Resource input, CancelIndicator monitor) { + List issues = cache.getOrUpdateIssues(resVal, input, CheckMode.ALL, monitor); + if (issues == null || input instanceof N4JSResource && !((N4JSResource) input).isFullyProcessed()) { + // Cancellation occurred likely before all validations completed, thus can't assume absence of errors. + // Cancellation may result in exit via normal control-flow (this case) or via exceptional control-flow (see + // exception handler below) + warnDueToCancelation(input, null); + return false; + } + + List errors = toList(filter(issues, i -> i.getSeverity() == ERROR)); + if (errors.isEmpty()) { + return true; + } + if (LOGGER.isDebugEnabled()) { + for (Issue it : errors) { + LOGGER.debug(input.getURI() + " " + it.getMessage() + " " + it.getSeverity() + " @L_" + + it.getLineNumber() + " "); + } + } + return false; + } + + private void warnDueToCancelation(Resource input, Throwable exc) { + String msg = "User canceled the validation of " + input.getURI() + ". Will not compile."; + if (null == exc) { + LOGGER.warn(msg); + } else { + LOGGER.warn(msg, exc); + } + } + + /** @return true iff the current project has a project type that is supposed to generate code. */ + private boolean isGenerateProjectType(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { + N4JSProjectConfigSnapshot project = ws.findProjectContaining(n4jsSourceURI); + if (project != null) { + ProjectType projectType = project.getType(); + if (N4JSGlobals.PROJECT_TYPES_WITHOUT_GENERATION.contains(projectType)) { + return false; + } + } + + return true; + } + + /** @return true iff the given resource does not lie within the output folder. */ + private boolean isOutsideOfOutputFolder(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { + return !containmentHelper.isContainedInOutputFolder(ws, n4jsSourceURI); + } + + /** @return true iff the output folder of the given n4js resource is not contained by a source container. */ + private boolean isOutputNotInSourceContainer(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { + N4JSProjectConfigSnapshot project = ws.findProjectContaining(n4jsSourceURI); + if (project != null) { + return !containmentHelper.isOutputContainedInSourceContainer(ws, project); + } else { + return false; + } + } + + /** + * Actual generation to be overridden by subclasses. + */ + abstract protected void internalDoGenerate(N4JSWorkspaceConfigSnapshot ws, Resource resource, + GeneratorOption[] options, IFileSystemAccess access); + + /** + * Returns the name of the target file (without path) to which the source is to be compiled to. Default + * implementation returns a configured project Name with version + file name + extension. E.g., "proj-0.0.1/p/A.js" + * for a file A in proj. + * + * Convenience method, to provide all necessary API for the sub-classes. Delegates to + * {@link ResourceNameComputer#generateFileDescriptor(Resource, String)}. + */ + protected String getTargetFileName(Resource n4jsSourceFile, String compiledFileExtension) { + return resourceNameComputer.generateFileDescriptor(n4jsSourceFile, compiledFileExtension); + } + + /** + * Convenient access to the Script-Element + */ + protected void rootElement(Resource resource) { + head(filter(resource.getContents(), Script.class)); + } + + /** The file-extension of the compiled result */ + protected String getCompiledFileExtension(Resource input) { + return preferenceAccess.getPreference(input, getCompilerID(), CompilerProperties.COMPILED_FILE_EXTENSION, + getDefaultDescriptor()); + } + + /** The file-extension of the source-map to the compiled result */ + protected String getCompiledFileSourceMapExtension(Resource input) { + return preferenceAccess.getPreference(input, getCompilerID(), + CompilerProperties.COMPILED_FILE_SOURCEMAP_EXTENSION, + getDefaultDescriptor()); + } + + /** Adjust output-path of the generator to match the N4JS projects-settings. */ + private void updateOutputPath(N4JSWorkspaceConfigSnapshot ws, IFileSystemAccess fsa, String compilerID, + Resource input) { + String outputPath = getOutputPath(ws, input.getURI()); + if (outputPath == null) { + outputPath = N4JSLanguageConstants.DEFAULT_PROJECT_OUTPUT; + } + if (fsa instanceof AbstractFileSystemAccess) { + OutputConfiguration conf = ((AbstractFileSystemAccess) fsa).getOutputConfigurations().get(compilerID); + if (conf != null) { + conf.setOutputDirectory(outputPath); + } + } + } + + private static String getOutputPath(N4JSWorkspaceConfigSnapshot ws, URI nestedLocation) { + if (ws.findProjectContaining(nestedLocation) == null) { + return null; + } + if (ws.findProjectContaining(nestedLocation).getProjectDescription() == null) { + return null; + } + return ws.findProjectContaining(nestedLocation).getProjectDescription().getOutputPath(); + } + + /** Navigation from the generated output-location to the location of the input-resource */ + @SuppressWarnings("unused") + protected Path calculateNavigationFromOutputToSourcePath(N4JSWorkspaceConfigSnapshot ws, IFileSystemAccess fsa, + String compilerID, N4JSResource input) { + // --- Project locations --- + N4JSProjectConfigSnapshot project = ws.findProjectContaining(input.getURI()); + + // /home/user/workspace/Project/ + Path projectPath = project.getPathAsFileURI().toFileSystemPath(); + // platform:/resource/Project/ + URI projectLocURI = project.getPathAsFileURI().withTrailingPathDelimiter().toURI(); + + // --- output locations --- + // src-gen + String outputPath = project.getOutputPath(); + // Project/a/b/c + Path outputRelativeLocation = getOutputRelativeLocation(input); + + // --- source locations --- + // src/a/b/c + URI inputURI = uriExtensions.withEmptyAuthority(input.getURI()); + URI completetSourceURI = inputURI.trimSegments(1).deresolve(projectLocURI); + String completetSource = URIUtils.toFile(completetSourceURI).toString(); + + // Handling case when source container is the project root itself. (Sources { source { '.' } }) + if (null == completetSource && project.getPathAsFileURI().toURI() == input.getURI().trimSegments(1)) { + completetSource = projectPath.toFile().getAbsolutePath(); + } + + // /home/user/workspace/Project/src-gen/a/b/c + Path fullOutpath = projectPath.resolve(outputPath).normalize().resolve(outputRelativeLocation).normalize(); + // /home/user/workspace/Project/src/a/b/c + Path fullSourcePath = projectPath.resolve(completetSource).normalize(); + + // ../../../../../../src/a/b/c + Path rel = fullOutpath.relativize(fullSourcePath); + + return rel; + } + + /** + * Calculates local output path for a given resource. Depending on the configuration this path can be in various + * forms, {@code Project-1.0.0/a/b/c/}, {@code Project/a/b/c/} or just {@code a/b/c/} + * + */ + private Path getOutputRelativeLocation(N4JSResource input) { + URI uri = uriExtensions.withEmptyAuthority(input.getURI()); + // Project/a/b/c/Input.XX + Path localOutputFilePath = Paths.get(resourceNameComputer.generateFileDescriptor(input, uri, ".XX")); + + // if calculated path has just one element, e.g. "Input.XX" + // then local path segment is empty + if (localOutputFilePath.getNameCount() < 2) { + return Paths.get(""); + } + + // otherwise strip resource to get local path, i.e. Project/a/b/c/Input.XX => Project/a/b/c/ + return localOutputFilePath.subpath(0, localOutputFilePath.getNameCount() - 1); + } + + /** + * TODO IDE-1487 currently there is no notion of default compiler. We fake call to the ES5 sub generator. + */ + final static String calculateProjectBasedOutputDirectory(N4JSProjectConfigSnapshot project, + boolean includeProjectName) { + return (includeProjectName) ? project.getPackageName() + "/" + project.getOutputPath() + : project.getOutputPath(); + } + + /** Access to compiler ID */ + abstract String getCompilerID(); + + /** Access to compiler descriptor */ + abstract CompilerDescriptor getDefaultDescriptor(); + + /** Answers: Is this compiler activated for the input at hand? */ + boolean isActive(Resource input) { + return Boolean.valueOf(preferenceAccess.getPreference(input, getCompilerID(), CompilerProperties.IS_ACTIVE, + getDefaultDescriptor())); + } + + /** + * Checking the availability of a static polyfill, which will override the compilation of this module. + **/ + boolean isNotStaticallyPolyfilled(Resource resource) { + // val TModule tmodule = (N4JSResource::getModule(resource) ); // for some reason xtend cannot see static + // getModule ?! + if (resource instanceof N4JSResource) { + TModule tmodule = N4JSResource.getModule(resource); + // 1. resource must be StaticPolyfillAware and + // 2. there must exist a polyfilling instance (second instance with same fqn) + if (tmodule.isStaticPolyfillAware()) { + // search for second instance. + if (staticPolyfillHelper.hasStaticPolyfill(resource)) { + return false; + } + } + } + return true; + } + + /** + * Checking if this resource represents a static polyfill, which will contribute to a filled resource. + **/ + private boolean isStaticPolyfillingModule(Resource resource) { + TModule tmodule = (N4JSResource.getModule(resource)); + if (null != tmodule) { + return tmodule.isStaticPolyfillModule(); + } + return false; + } + + /** + * + * @return true if the composite generator is applicable to the given resource and false otherwise. + */ + @Override + public boolean isApplicableTo(Resource input) { + return shouldBeCompiled(input, null); + } + + /** + * Depending on the file-extension, determines if the given resource requires actual transpilation as opposed to + * simply copying the source file to the output folder. + * + * @param eResource + * N4JS resource to check. + * @return true if the code requires transpilation. + */ + protected boolean justCopy(Resource eResource) { + ResourceType resourceType = ResourceType.getResourceType(eResource); + return !(resourceType.equals(ResourceType.N4JS) || resourceType.equals(ResourceType.N4JSX) + || resourceType.equals(ResourceType.N4JSD)); + } + + /** + * Take the content of resource and copy it over to the output folder without any transformation. + * + * @param resource + * JS-code snippet which will be treated as text. + * @param fsa + * file system access + */ + protected void copyWithoutTranspilation(Resource resource, IFileSystemAccess fsa) { + StringWriter outCode = new StringWriter(); + // get script + EObject script = resource.getContents().get(0); + + // obtain text + String scriptAsText = NodeModelUtils.getNode(script).getRootNode().getText(); + + // write + String decorated = scriptAsText.toString(); + outCode.write(decorated); + String filename = resourceNameComputer.generateFileDescriptor(resource, + URIUtils.fileExtension(resource.getURI())); + fsa.generateFile(filename, getCompilerID(), outCode.toString()); + } +} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/generator/AbstractSubGenerator.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/generator/AbstractSubGenerator.xtend deleted file mode 100644 index 56c17a092f..0000000000 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/generator/AbstractSubGenerator.xtend +++ /dev/null @@ -1,482 +0,0 @@ -/** - * Copyright (c) 2016 NumberFour AG. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * NumberFour AG - Initial API and implementation - */ -package org.eclipse.n4js.generator - -import com.google.inject.Inject -import java.io.StringWriter -import java.nio.file.Path -import java.nio.file.Paths -import java.util.List -import org.eclipse.emf.common.EMFPlugin -import org.eclipse.emf.common.util.URI -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.n4js.N4JSGlobals -import org.eclipse.n4js.N4JSLanguageConstants -import org.eclipse.n4js.generator.IGeneratorMarkerSupport.Severity -import org.eclipse.n4js.n4JS.Script -import org.eclipse.n4js.resource.N4JSCache -import org.eclipse.n4js.resource.N4JSResource -import org.eclipse.n4js.ts.types.TModule -import org.eclipse.n4js.utils.FolderContainmentHelper -import org.eclipse.n4js.utils.Log -import org.eclipse.n4js.utils.ResourceNameComputer -import org.eclipse.n4js.utils.ResourceType -import org.eclipse.n4js.utils.StaticPolyfillHelper -import org.eclipse.n4js.utils.URIUtils -import org.eclipse.n4js.workspace.N4JSProjectConfigSnapshot -import org.eclipse.n4js.workspace.N4JSWorkspaceConfigSnapshot -import org.eclipse.n4js.workspace.WorkspaceAccess -import org.eclipse.xtend.lib.annotations.Accessors -import org.eclipse.xtext.generator.AbstractFileSystemAccess -import org.eclipse.xtext.generator.IFileSystemAccess -import org.eclipse.xtext.generator.IFileSystemAccess2 -import org.eclipse.xtext.generator.IGenerator2 -import org.eclipse.xtext.generator.IGeneratorContext -import org.eclipse.xtext.nodemodel.util.NodeModelUtils -import org.eclipse.xtext.service.OperationCanceledManager -import org.eclipse.xtext.util.CancelIndicator -import org.eclipse.xtext.util.UriExtensions -import org.eclipse.xtext.validation.CheckMode -import org.eclipse.xtext.validation.IResourceValidator -import org.eclipse.xtext.validation.Issue - -import static org.eclipse.xtext.diagnostics.Severity.* - -/** - * All sub generators should extend this class. It provides basic blocks of the logic, and - * shared implementations. - */ -@Log -abstract class AbstractSubGenerator implements ISubGenerator, IGenerator2 { - - @Accessors - private CompilerDescriptor compilerDescriptor = null - - @Inject protected StaticPolyfillHelper staticPolyfillHelper - - @Inject protected WorkspaceAccess workspaceAccess - - @Inject protected ResourceNameComputer resourceNameComputer - - @Inject protected IResourceValidator resVal - - @Inject protected N4JSCache cache - - @Inject protected IGeneratorMarkerSupport genMarkerSupport - - @Inject protected OperationCanceledManager operationCanceledManager - - @Inject protected GeneratorExceptionHandler exceptionHandler - - @Inject protected N4JSPreferenceAccess preferenceAccess - - @Inject private FolderContainmentHelper containmentHelper; - - @Inject private UriExtensions uriExtensions; - - - override getCompilerDescriptor() { - if (compilerDescriptor === null) { - compilerDescriptor = getDefaultDescriptor - } - return compilerDescriptor - } - - - override doGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context) { - doGenerate(input, fsa); - } - - override beforeGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context) { - // TODO Auto-generated method stub - - } - - override afterGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context) { - // TODO Auto-generated method stub - - } - - /** - * This override declares an {@link IFileSystemAccess} parameter. At runtime, its actual type depends on whether IDE or headless, - * which in turn determines whether the actual argument has a progress monitor or not: - *
    - *
  • - * IDE scenario: Actual type is {@code EclipseResourceFileSystemAccess2}. A progress monitor can be obtained via {@code getMonitor()}. - * It is checked automatically behind the scenes, for example in {@code generateFile()}. - * Upon detecting a pending cancellation request, an {@code OperationCanceledException} is raised. - *
  • - *
  • - * Headless scenario: Actual type is {@code JavaIoFileSystemAccess}. No progress monitor is available. - *
  • - *
- */ - override doGenerate(Resource input, IFileSystemAccess fsa) { - if (justCopy(input)) { - copyWithoutTranspilation(input, fsa); - return; - } - - val ws = workspaceAccess.getWorkspaceConfig(input); - - try { - // remove error-marker - genMarkerSupport.deleteMarker(input) - - updateOutputPath(ws, fsa, getCompilerID, input); - internalDoGenerate(ws, input, GeneratorOption.DEFAULT_OPTIONS, fsa); - - } catch (UnresolvedProxyInSubGeneratorException e) { - genMarkerSupport.createMarker(input, e.message, Severity.ERROR); - - } catch (Exception e) { - // cancellation is not an error case, so simply propagate as usual - operationCanceledManager.propagateIfCancelException(e); - - // issue error marker - val target = (if (input instanceof N4JSResource) input.module?.moduleSpecifier) ?: input.URI?.toString; - val msgMarker = "Severe error occurred while transpiling module " + target - + ". Check error log for details about the failure."; - genMarkerSupport.createMarker(input, msgMarker, Severity.ERROR); - - // re-throw as GeneratorException to have the frameworks notify the error. - if (e instanceof GeneratorException) { - throw e; - } - var msg = if (e.message === null) "type=" + e.class.name else "message=" + e.message; - exceptionHandler.handleError('''Severe error occurred in transpiler=«compilerID» «msg».''', e); - } - } - - override shouldBeCompiled(Resource input, CancelIndicator monitor) { - val ws = workspaceAccess.getWorkspaceConfig(input); - - val autobuildEnabled = isActive(input) - val isXPECTMode = N4JSGlobals.XT_FILE_EXTENSION.equals(URIUtils.fileExtension(input.URI).toLowerCase); - val inputUri = input.URI - - val boolean result = (autobuildEnabled - && isGenerateProjectType(ws, inputUri) - && hasOutput(ws, inputUri) - && isOutputNotInSourceContainer(ws, inputUri) - && isOutsideOfOutputFolder(ws, inputUri) - && isSource(ws, inputUri) - && (isNoValidate(ws, inputUri) - || isExternal(ws, inputUri) - // if platform is running (but not in XPECT mode) the generator is called from the builder, hence cannot have any validation errors - // (note: XPECT swallows errors hence we cannot rely on Eclipse in case of .xt files) - || ((EMFPlugin.IS_ECLIPSE_RUNNING && !isXPECTMode) || hasNoErrors(input, monitor)) - )) - && (!input.isStaticPolyfillingModule) // compile driven by filled type - && hasNoPolyfillErrors(input,monitor) - return result - } - - private def hasOutput(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI){ - return getOutputPath(ws, n4jsSourceURI) !== null; - } - private def isSource(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { - return ws.findSourceFolderContaining(n4jsSourceURI) !== null; - } - - private def isNoValidate(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { - return ws.isNoValidate(n4jsSourceURI); - } - - private def isExternal(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { - val sourceContainerOpt = ws.findSourceFolderContaining(n4jsSourceURI); - if (sourceContainerOpt !== null) { - val sourceContainer = sourceContainerOpt; - return sourceContainer.external; - } - return false; - } - - /** If the resource has a static polyfill, then ensure it is error-free. - * Calls {@link #hasNoErrors()} on the static polyfill resource. - */ - private def boolean hasNoPolyfillErrors(Resource input, CancelIndicator monitor) { - val resSPoly = staticPolyfillHelper.getStaticPolyfillResource(input) - if (resSPoly === null) { - return true; - } - // re-validation is necessary since the changes of the current resource (i.e. filled resource) - // can affect the filling resource in a way that validation errors will be removed or created. - cache.recreateIssues(resVal, resSPoly, CheckMode.ALL, monitor); - return hasNoErrors(resSPoly, monitor) - } - - /** - * Does validation report no errors for the given resource? - * If errors exists, log them as a side-effect. - * If validation was canceled before finishing, don't assume absence of errors. - */ - private def boolean hasNoErrors(Resource input, CancelIndicator monitor) { - val List issues = cache.getOrUpdateIssues(resVal, input, CheckMode.ALL, monitor); - if (issues === null || input instanceof N4JSResource && !(input as N4JSResource).isFullyProcessed) { - // Cancellation occurred likely before all validations completed, thus can't assume absence of errors. - // Cancellation may result in exit via normal control-flow (this case) or via exceptional control-flow (see exception handler below) - warnDueToCancelation(input, null); - return false; - } - - val Iterable errors = issues.filter[severity == ERROR]; - if (errors.isEmpty()) { - return true - } - if (logger.isDebugEnabled) { - errors.forEach[logger.debug(input.URI + " " + it.message + " " + it.severity + " @L_" + it.lineNumber + " ")] - } - return false - } - - private def void warnDueToCancelation(Resource input, Throwable exc) { - val msg = "User canceled the validation of " + input.URI + ". Will not compile."; - if (null === exc) { - logger.warn(msg) - } else { - logger.warn(msg,exc) - } - } - - /** @return true iff the current project has a project type that is supposed to generate code. */ - private def boolean isGenerateProjectType(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { - val project = ws.findProjectContaining(n4jsSourceURI); - if (project !== null) { - val projectType = project.getType(); - if (N4JSGlobals.PROJECT_TYPES_WITHOUT_GENERATION.contains(projectType)) { - return false; - } - } - - return true; - } - - /** @return true iff the given resource does not lie within the output folder. */ - private def boolean isOutsideOfOutputFolder(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { - return !containmentHelper.isContainedInOutputFolder(ws, n4jsSourceURI); - } - - /** @return true iff the output folder of the given n4js resource is not contained by a source container. */ - private def boolean isOutputNotInSourceContainer(N4JSWorkspaceConfigSnapshot ws, URI n4jsSourceURI) { - val project = ws.findProjectContaining(n4jsSourceURI); - if (project !== null) { - return !containmentHelper.isOutputContainedInSourceContainer(ws, project) - } else { - return false; - } - } - - /** - * Actual generation to be overridden by subclasses. - */ - protected def void internalDoGenerate(N4JSWorkspaceConfigSnapshot ws, Resource resource, GeneratorOption[] options, IFileSystemAccess access) - - /** - * Returns the name of the target file (without path) to which the source is to be compiled to. - * Default implementation returns a configured project Name with version + file name + extension. - * E.g., "proj-0.0.1/p/A.js" for a file A in proj. - * - * Convenience method, to provide all necessary API for the sub-classes. - * Delegates to {@link ResourceNameComputer#getTargetFileName}. - */ - protected def String getTargetFileName(Resource n4jsSourceFile, String compiledFileExtension) { - return resourceNameComputer.generateFileDescriptor(n4jsSourceFile, compiledFileExtension) - } - - /** - * Convenient access to the Script-Element - */ - protected def rootElement(Resource resource) { - resource.contents.filter(Script).head - } - - /** The file-extension of the compiled result */ - protected def String getCompiledFileExtension(Resource input) { - preferenceAccess.getPreference(input, getCompilerID(), CompilerProperties.COMPILED_FILE_EXTENSION, - getDefaultDescriptor()) - } - - /** The file-extension of the source-map to the compiled result */ - protected def String getCompiledFileSourceMapExtension(Resource input) { - preferenceAccess.getPreference(input, getCompilerID(), CompilerProperties.COMPILED_FILE_SOURCEMAP_EXTENSION, - getDefaultDescriptor()) - } - - /** Adjust output-path of the generator to match the N4JS projects-settings. */ - private def void updateOutputPath(N4JSWorkspaceConfigSnapshot ws, IFileSystemAccess fsa, String compilerID, Resource input) { - val outputPath = getOutputPath(ws, input.URI) ?: N4JSLanguageConstants.DEFAULT_PROJECT_OUTPUT; - if (fsa instanceof AbstractFileSystemAccess) { - val conf = fsa.outputConfigurations.get(compilerID) - if (conf !== null) { - conf.setOutputDirectory(outputPath) - } - } - } - - private static def String getOutputPath(N4JSWorkspaceConfigSnapshot ws, URI nestedLocation) { - return ws.findProjectContaining(nestedLocation)?.getProjectDescription()?.getOutputPath(); - } - - /** Navigation from the generated output-location to the location of the input-resource */ - protected def Path calculateNavigationFromOutputToSourcePath(N4JSWorkspaceConfigSnapshot ws, IFileSystemAccess fsa, String compilerID, N4JSResource input) { - // --- Project locations --- - val project = ws.findProjectContaining(input.URI); - - // /home/user/workspace/Project/ - val projectPath = project.pathAsFileURI.toFileSystemPath - // platform:/resource/Project/ - val projectLocURI = project.pathAsFileURI.withTrailingPathDelimiter.toURI - - // --- output locations --- - // src-gen - val outputPath = project.outputPath - // Project/a/b/c - val outputRelativeLocation = getOutputRelativeLocation(input) - - // --- source locations --- - // src/a/b/c - val inputURI = uriExtensions.withEmptyAuthority(input.URI); - val completetSourceURI = inputURI.trimSegments(1).deresolve(projectLocURI) - var completetSource = URIUtils.toFile(completetSourceURI).toString(); - - // Handling case when source container is the project root itself. (Sources { source { '.' } }) - if (null === completetSource && project.pathAsFileURI.toURI === input.URI.trimSegments(1)) { - completetSource = projectPath.toFile.absolutePath; - } - - // /home/user/workspace/Project/src-gen/a/b/c - val fullOutpath = projectPath.resolve(outputPath).normalize.resolve(outputRelativeLocation).normalize - // /home/user/workspace/Project/src/a/b/c - val fullSourcePath = projectPath.resolve(completetSource).normalize - - // ../../../../../../src/a/b/c - val rel = fullOutpath.relativize(fullSourcePath) - - return rel - } - - /** - * Calculates local output path for a given resource. - * Depending on the configuration this path can be in various forms, {@code Project-1.0.0/a/b/c/}, - * {@code Project/a/b/c/} or just {@code a/b/c/} - * - */ - private def Path getOutputRelativeLocation(N4JSResource input){ - val uri = uriExtensions.withEmptyAuthority(input.URI); - // Project/a/b/c/Input.XX - val localOutputFilePath = Paths.get(resourceNameComputer.generateFileDescriptor(input, uri, ".XX")) - - // if calculated path has just one element, e.g. "Input.XX" - // then local path segment is empty - if(localOutputFilePath.nameCount<2){ - return Paths.get("") - } - - //otherwise strip resource to get local path, i.e. Project/a/b/c/Input.XX => Project/a/b/c/ - return localOutputFilePath.subpath(0,localOutputFilePath.nameCount - 1) - } - - /** - * Convenience for {@link AbstractSubGenerator#calculateOutputDirectory(String, String)}, - * uses default compiler ID. - * - * TODO IDE-1487 currently there is no notion of default compiler. We fake call to the ES5 sub generator. - */ - def final static String calculateProjectBasedOutputDirectory(N4JSProjectConfigSnapshot project, boolean includeProjectName) { - return if (includeProjectName) project.packageName + "/" + project.outputPath else project.outputPath; - } - - /** Access to compiler ID */ - def abstract String getCompilerID() - - /** Access to compiler descriptor */ - def abstract CompilerDescriptor getDefaultDescriptor() - - /** Answers: Is this compiler activated for the input at hand? */ - def boolean isActive(Resource input) { - return Boolean.valueOf(preferenceAccess.getPreference(input, getCompilerID(), CompilerProperties.IS_ACTIVE, getDefaultDescriptor())) - } - - /** Checking the availability of a static polyfill, which will override the compilation of this module. - **/ - def boolean isNotStaticallyPolyfilled(Resource resource) { - // val TModule tmodule = (N4JSResource::getModule(resource) ); // for some reason xtend cannot see static getModule ?! - if (resource instanceof N4JSResource) { - val TModule tmodule = N4JSResource.getModule(resource) - // 1. resource must be StaticPolyfillAware and - // 2. there must exist a polyfilling instance (second instance with same fqn) - if (tmodule.isStaticPolyfillAware) { - // search for second instance. - if (staticPolyfillHelper.hasStaticPolyfill(resource)) { - return false; - } - } - } - return true; - } - - /** - * Checking if this resource represents a static polyfill, which will contribute to a filled resource. - **/ - private def boolean isStaticPolyfillingModule(Resource resource) { - val TModule tmodule = (N4JSResource.getModule(resource)); - if (null !== tmodule) { - return tmodule.isStaticPolyfillModule; - } - return false; - } - - /** - * - * @return true if the composite generator is applicable to the given resource and false otherwise. - */ - override boolean isApplicableTo(Resource input) { - return shouldBeCompiled(input, null); - } - - /** - * Depending on the file-extension, determines if the given resource requires actual transpilation as opposed to - * simply copying the source file to the output folder. - * - * @param eResource - * N4JS resource to check. - * @return true if the code requires transpilation. - */ - def protected boolean justCopy(Resource eResource) { - val resourceType = ResourceType.getResourceType(eResource); - return !(resourceType.equals(ResourceType.N4JS) || resourceType.equals(ResourceType.N4JSX) - || resourceType.equals(ResourceType.N4JSD)); - } - - /** - * Take the content of resource and copy it over to the output folder without any transformation. - * - * @param resource - * JS-code snippet which will be treated as text. - * @param fsa - * file system access - */ - def protected void copyWithoutTranspilation(Resource resource, IFileSystemAccess fsa) { - val outCode = new StringWriter(); - // get script - val script = resource.getContents().get(0); - - // obtain text - val scriptAsText = NodeModelUtils.getNode(script).getRootNode().getText(); - - // write - val decorated = scriptAsText.toString(); - outCode.write(decorated); - val filename = resourceNameComputer.generateFileDescriptor(resource, URIUtils.fileExtension(resource.getURI())); - fsa.generateFile(filename, compilerID, outCode.toString()); - } -} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/packagejson/PackageJsonBuilder.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/packagejson/PackageJsonBuilder.java new file mode 100644 index 0000000000..c91ec98e79 --- /dev/null +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/packagejson/PackageJsonBuilder.java @@ -0,0 +1,380 @@ +/** + * Copyright (c) 2016 NumberFour AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * NumberFour AG - Initial API and implementation + */ +package org.eclipse.n4js.packagejson; + +import static com.google.common.base.Optional.fromNullable; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.eclipse.n4js.json.JSON.JSONDocument; +import org.eclipse.n4js.json.model.utils.JSONModelUtils; +import org.eclipse.n4js.packagejson.projectDescription.ProjectType; +import org.eclipse.n4js.packagejson.projectDescription.SourceContainerType; +import org.eclipse.n4js.utils.ProjectDescriptionLoader; +import org.eclipse.n4js.workspace.utils.N4JSPackageName; + +/** + * Convenient builder for creating the N4JS package.json compliant {@link JSONDocument} model instances or file + * content.# + * + * Provides support for most supported N4JS-specific package.json properties. See {@link ProjectDescriptionLoader} for + * details on all supported properties. + */ +@SuppressWarnings("hiding") +public class PackageJsonBuilder { + + /** + * Creates a new builder instance with a default project type of {@link ProjectType#VALIDATION}. + */ + public static PackageJsonBuilder newBuilder() { + return new PackageJsonBuilder(ProjectType.VALIDATION); // use project type 'validation' + } + + /** + * Creates a new builder instance without a default project type. Use this for creating a plain package.json without + * an "n4js" section. + */ + public static PackageJsonBuilder newPlainBuilder() { + return new PackageJsonBuilder(null); + } + + private String projectName; + private ProjectType type; + private String version; + private Boolean _private; + + private final SortedMap dependencies; + private final SortedMap devDependencies; + + private String vendorId; + private String vendorName; + + private String output; + + private final Collection providedRLs; + private final Collection requiredRLs; + private final Collection testedProjects; + private String extendedRE; + private final Collection implementedProjects; + private String implementationId; + + private final Map sourceContainers; + + private final Collection workspaces; + + private PackageJsonBuilder(ProjectType defaultProjectType) { + type = defaultProjectType; + providedRLs = new ArrayList<>(); + requiredRLs = new ArrayList<>(); + dependencies = new TreeMap<>(); + devDependencies = new TreeMap<>(); + implementedProjects = new ArrayList<>(); + testedProjects = new ArrayList<>(); + sourceContainers = new HashMap<>(); + workspaces = new ArrayList<>(); + } + + /** + * Builds the N4JS package.json file contents for a project with the given name. + * + * @return the N4JS package.json file contents as a string. + */ + public String build() { + JSONDocument document = this.buildModel(); + return JSONModelUtils.serializeJSON(document); + } + + /** + * Builds the N4JS package.json {@code JSONDocument} model representation. + * + * @return the N4JS package.json {@code JSONDocument} representation. + */ + public JSONDocument buildModel() { + return PackageJsonContentProvider.getModel( + fromNullable(projectName), + fromNullable(version), + fromNullable(_private), + workspaces, + fromNullable(type), + fromNullable(vendorId), + fromNullable(vendorName), + fromNullable(output), + fromNullable(extendedRE), + dependencies, + devDependencies, + providedRLs, + requiredRLs, + fromNullable(implementationId), + implementedProjects, + testedProjects, + sourceContainers); + } + + /** + * Sets the project name and returns with the builder. + * + * @param name + * The N4JS project name. + * @return the builder. + */ + public PackageJsonBuilder withName(String name) { + this.projectName = checkNotNull(name); + return this; + } + + /** + * Sets the project version and returns with the builder. + * + * @param version + * The project version. + * @return the builder. + */ + public PackageJsonBuilder withVersion(String version) { + this.version = checkNotNull(version); + return this; + } + + /** + * Adds top-level property "private" to the package.json, with the given value. + */ + public PackageJsonBuilder withPrivate(boolean _private) { + this._private = _private; + return this; + } + + /** + * Sets the project type and returns with the builder. + * + * @param type + * the N4JS project type. + * @return the builder. + */ + public PackageJsonBuilder withType(ProjectType type) { + this.type = checkNotNull(type); + return this; + } + + /** + * Sets the vendorId and returns with the builder. + * + * @param vendorId + * The project's vendor ID. + * @return the builder. + */ + public PackageJsonBuilder withVendorId(String vendorId) { + this.vendorId = vendorId; + return this; + } + + /** + * Sets the vendor name and returns with the builder. + * + * @param vendorName + * The project's vendor name. + * @return the builder. + */ + public PackageJsonBuilder withVendorName(String vendorName) { + this.vendorName = vendorName; + return this; + } + + /** + * Sets the output folder and returns with the builder. + * + * @param output + * The project's vendor name. + * @return the builder. + */ + public PackageJsonBuilder withOutput(String output) { + this.output = output; + return this; + } + + /** + * Adds a source container with the given folder specifier and type. + * + * @param type + * The source container type. + * @param path + * The source container path. + */ + public PackageJsonBuilder withSourceContainer(SourceContainerType type, String path) { + this.sourceContainers.put(checkNotNull(type), checkNotNull(path)); + return this; + } + + /** + * Sets the extended runtime environment on the builder. + * + * @param extendedRE + * the extended runtime environment. Optional. Can be {@code null}. + * @return the builder. + */ + public PackageJsonBuilder withExtendedRE(String extendedRE) { + this.extendedRE = extendedRE; + return this; + } + + /** + * Adds a new provided runtime library to the current builder. + * + * @param providedRL + * the provided runtime library to add to the current builder. Cannot be {@code null}. + * @return the builder. + */ + public PackageJsonBuilder withProvidedRL(String providedRL) { + providedRLs.add(checkNotNull(providedRL)); + return this; + } + + /** + * Adds a new required runtime library to the current builder. + * + * This method will add the required runtime library both to the "requiredRuntimeLibraries" section, as well as the + * "dependencies" section. + * + * @param requiredRL + * the required runtime library to add to the current builder. Cannot be {@code null}. + * @return the builder. + */ + public PackageJsonBuilder withRequiredRL(String requiredRL) { + requiredRLs.add(checkNotNull(requiredRL)); + // also add to dependencies + dependencies.put(requiredRL, "*"); + return this; + } + + /** + * Adds a new required runtime library with the given version constraint. + * + * This method will add the required runtime library both to the "requiredRuntimeLibraries" section, as well as the + * "dependencies" section. + * + * @param requiredRL + * The project id of the required runtime library. + * @param versionConstraint + * The version constraint to be used in the dependency section. + */ + public PackageJsonBuilder withRequiredRL(String requiredRL, String versionConstraint) { + requiredRLs.add(checkNotNull(requiredRL)); + // also add to dependencies + dependencies.put(requiredRL, versionConstraint); + return this; + } + + /** + * Adds a new project dependency to the current builder. + * + * @param projectDependency + * the project dependency to add to the current builder. Cannot be {@code null}. + * @return the builder. + */ + public PackageJsonBuilder withDependency(N4JSPackageName projectDependency) { + dependencies.put(checkNotNull(projectDependency).getRawName(), "*"); + return this; + } + + /** + * Adds a new project dependency to the current builder. + * + * @param projectDependency + * The project dependency to add to the current builder. Cannot be {@code null}. + * @param versionConstraint + * The version constraint of the added project dependency. + * + * @return the builder. + */ + public PackageJsonBuilder withDependency(N4JSPackageName projectDependency, String versionConstraint) { + dependencies.put(checkNotNull(projectDependency).getRawName(), checkNotNull(versionConstraint)); + return this; + } + + /** + * Adds a new project devDependency to the current builder. + * + * @param projectDevDependency + * the project devDependency to add to the current builder. Cannot be {@code null}. + * @return the builder. + */ + public PackageJsonBuilder withDevDependency(N4JSPackageName projectDevDependency) { + devDependencies.put(checkNotNull(projectDevDependency).getRawName(), "*"); + return this; + } + + /** + * Adds a new project devDependency to the current builder. + * + * @param projectDevDependency + * The project devDependency to add to the current builder. Cannot be {@code null}. + * @param versionConstraint + * The version constraint of the added project devDependency. + * + * @return the builder. + */ + public PackageJsonBuilder withDevDependency(String projectDevDependency, String versionConstraint) { + devDependencies.put(checkNotNull(projectDevDependency), checkNotNull(versionConstraint)); + return this; + } + + /** + * Adds the given project id to the list of tested projects. + * + * This method also adds the given tested project as project dependency. + */ + public PackageJsonBuilder withTestedProject(String testedProject) { + dependencies.put(checkNotNull(testedProject), "*"); + testedProjects.add(checkNotNull(testedProject)); + return this; + } + + /** + * Sets the implementation id to the current builder. + * + * @param implementationId + * id for the implementations to choose from. + * @return the builder. + */ + public PackageJsonBuilder withImplementationId(String implementationId) { + this.implementationId = checkNotNull(implementationId); + return this; + } + + /** + * Adds a project to the list of implemented Projects. + * + * @param implementationAPI + * id of the implemented API + * @return the builder. + */ + public PackageJsonBuilder withImplementedProject(String implementationAPI) { + implementedProjects.add(checkNotNull(implementationAPI)); + return this; + } + + /** Adds top-level property "workspaces" to the package.json, using an array with the given strings as value. */ + public PackageJsonBuilder withWorkspaces(String... workspaces) { + this.workspaces.addAll(Arrays.asList(workspaces)); + return this; + } + + @Override + public String toString() { + return "!!! This is just a preview of the N4JS package.json file !!!\n" + this.build(); + } + +} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/packagejson/PackageJsonBuilder.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/packagejson/PackageJsonBuilder.xtend deleted file mode 100644 index b85a94da8c..0000000000 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/packagejson/PackageJsonBuilder.xtend +++ /dev/null @@ -1,358 +0,0 @@ -/** - * Copyright (c) 2016 NumberFour AG. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * NumberFour AG - Initial API and implementation - */ -package org.eclipse.n4js.packagejson - -import java.util.Collection -import java.util.Map -import java.util.SortedMap -import java.util.TreeMap -import org.eclipse.n4js.json.JSON.JSONDocument -import org.eclipse.n4js.json.model.utils.JSONModelUtils -import org.eclipse.n4js.packagejson.projectDescription.ProjectType -import org.eclipse.n4js.packagejson.projectDescription.SourceContainerType -import org.eclipse.n4js.utils.ProjectDescriptionLoader - -import static com.google.common.base.Optional.fromNullable -import static com.google.common.base.Preconditions.checkNotNull -import org.eclipse.n4js.workspace.utils.N4JSPackageName - -/** - * Convenient builder for creating the N4JS package.json compliant {@link JSONDocument} model - * instances or file content.# - * - * Provides support for most supported N4JS-specific package.json properties. - * See {@link ProjectDescriptionLoader} for details on all supported properties. - */ -public class PackageJsonBuilder { - - /** - * Creates a new builder instance with a default project type of {@link ProjectType#VALIDATION}. - */ - public static def PackageJsonBuilder newBuilder() { - return new PackageJsonBuilder(ProjectType.VALIDATION); // use project type 'validation' - } - - /** - * Creates a new builder instance without a default project type. Use this for creating a - * plain package.json without an "n4js" section. - */ - public static def PackageJsonBuilder newPlainBuilder() { - return new PackageJsonBuilder(null); - } - - private String projectName; - private ProjectType type; - private String version; - private Boolean _private; - - private SortedMap dependencies; - private SortedMap devDependencies; - - private String vendorId; - private String vendorName; - - private String output; - - private Collection providedRLs; - private Collection requiredRLs; - private Collection testedProjects; - private String extendedRE; - private Collection implementedProjects; - private String implementationId; - - private Map sourceContainers; - - private Collection workspaces; - - private new(ProjectType defaultProjectType) { - type = defaultProjectType; - providedRLs = newArrayList - requiredRLs = newArrayList - dependencies = new TreeMap(); - devDependencies = new TreeMap(); - implementedProjects = newArrayList - testedProjects = newArrayList; - sourceContainers = newHashMap; - workspaces = newArrayList; - } - - /** - * Builds the N4JS package.json file contents for a project with the given name. - * - * @return the N4JS package.json file contents as a string. - */ - def String build() { - val document = this.buildModel(); - return JSONModelUtils.serializeJSON(document); - } - - /** - * Builds the N4JS package.json {@code JSONDocument} model representation. - * - * @return the N4JS package.json {@code JSONDocument} representation. - */ - def JSONDocument buildModel() { - return PackageJsonContentProvider.getModel( - fromNullable(projectName), - fromNullable(version), - fromNullable(_private), - workspaces, - fromNullable(type), - fromNullable(vendorId), - fromNullable(vendorName), - fromNullable(output), - fromNullable(extendedRE), - dependencies, - devDependencies, - providedRLs, - requiredRLs, - fromNullable(implementationId), - implementedProjects, - testedProjects, - sourceContainers); - } - - /** - * Sets the project name and returns with the builder. - * - * @param type The N4JS project name. - * @return the builder. - */ - def PackageJsonBuilder withName(String name) { - this.projectName = checkNotNull(name) - return this; - } - - /** - * Sets the project version and returns with the builder. - * - * @param version The project version. - * @return the builder. - */ - def PackageJsonBuilder withVersion(String version) { - this.version = checkNotNull(version) - return this; - } - - /** - * Adds top-level property "private" to the package.json, with the given value. - */ - def PackageJsonBuilder withPrivate(boolean _private) { - this._private = _private; - return this; - } - - /** - * Sets the project type and returns with the builder. - * - * @param type the N4JS project type. - * @return the builder. - */ - def PackageJsonBuilder withType(ProjectType type) { - this.type = checkNotNull(type) - return this; - } - - /** - * Sets the vendorId and returns with the builder. - * - * @param type The project's vendor ID. - * @return the builder. - */ - def PackageJsonBuilder withVendorId(String vendorId) { - this.vendorId = vendorId; - return this; - } - - /** - * Sets the vendor name and returns with the builder. - * - * @param type The project's vendor name. - * @return the builder. - */ - def PackageJsonBuilder withVendorName(String vendorName) { - this.vendorName = vendorName; - return this; - } - - /** - * Sets the output folder and returns with the builder. - * - * @param type The project's vendor name. - * @return the builder. - */ - def PackageJsonBuilder withOutput(String output) { - this.output = output; - return this; - } - - /** - * Adds a source container with the given folder specifier and type. - * - * @param type The source container type. - * @param path The source container path. - */ - def PackageJsonBuilder withSourceContainer(SourceContainerType type, String path) { - this.sourceContainers.put(checkNotNull(type), checkNotNull(path)); - return this; - } - - /** - * Sets the extended runtime environment on the builder. - * @param extendedRE the extended runtime environment. Optional. Can be {@code null}. - * @return the builder. - */ - def PackageJsonBuilder withExtendedRE(String extendedRE) { - this.extendedRE = extendedRE - return this; - } - - /** - * Adds a new provided runtime library to the current builder. - * @param providedRL the provided runtime library to add to the current builder. Cannot be {@code null}. - * @return the builder. - */ - def PackageJsonBuilder withProvidedRL(String providedRL) { - providedRLs.add(checkNotNull(providedRL)) - return this; - } - - /** - * Adds a new required runtime library to the current builder. - * - * This method will add the required runtime library both to the "requiredRuntimeLibraries" section, - * as well as the "dependencies" section. - * - * @param requiredRL the required runtime library to add to the current builder. Cannot be {@code null}. - * @return the builder. - */ - def PackageJsonBuilder withRequiredRL(String requiredRL) { - requiredRLs.add(checkNotNull(requiredRL)) - // also add to dependencies - dependencies.put(requiredRL, "*") - return this; - } - - /** - * Adds a new required runtime library with the given version constraint. - * - * This method will add the required runtime library both to the "requiredRuntimeLibraries" section, - * as well as the "dependencies" section. - * - * @param requiredRL The project id of the required runtime library. - * @param versionConstraint The version constraint to be used in the dependency section. - */ - def PackageJsonBuilder withRequiredRL(String requiredRL, String versionConstraint) { - requiredRLs.add(checkNotNull(requiredRL)) - // also add to dependencies - dependencies.put(requiredRL, versionConstraint) - return this; - } - - /** - * Adds a new project dependency to the current builder. - * @param projectDependency the project dependency to add to the current builder. Cannot be {@code null}. - * @return the builder. - */ - def PackageJsonBuilder withDependency(N4JSPackageName projectDependency) { - dependencies.put(checkNotNull(projectDependency).rawName, "*") - return this; - } - - /** - * Adds a new project dependency to the current builder. - * - * @param projectDependency The project dependency to add to the current builder. Cannot be {@code null}. - * @param versionConstraint The version constraint of the added project dependency. - * - * @return the builder. - */ - def PackageJsonBuilder withDependency(N4JSPackageName projectDependency, String versionConstraint) { - dependencies.put(checkNotNull(projectDependency).rawName, checkNotNull(versionConstraint)) - return this; - } - - /** - * Adds a new project devDependency to the current builder. - * @param projectDevDependency the project devDependency to add to the current builder. Cannot be {@code null}. - * @return the builder. - */ - def PackageJsonBuilder withDevDependency(N4JSPackageName projectDevDependency) { - devDependencies.put(checkNotNull(projectDevDependency).rawName, "*") - return this; - } - - /** - * Adds a new project devDependency to the current builder. - * - * @param projectDevDependency The project devDependency to add to the current builder. Cannot be {@code null}. - * @param versionConstraint The version constraint of the added project devDependency. - * - * @return the builder. - */ - def PackageJsonBuilder withDevDependency(String projectDevDependency, String versionConstraint) { - devDependencies.put(checkNotNull(projectDevDependency), checkNotNull(versionConstraint)) - return this; - } - - /** - * Adds the given project id to the list of tested projects. - * - * This method also adds the given tested project as project dependency. - */ - def PackageJsonBuilder withTestedProject(String testedProject) { - dependencies.put(checkNotNull(testedProject), "*"); - testedProjects.add(checkNotNull(testedProject)); - return this; - } - - /** - * Adds the given project id to the list of tested projects and to the list - * of dependencies with the given version constraint. - * - * This method also adds the given tested project as project dependency. - */ - def PackageJsonBuilder withTestedProject(String testedProject, String version) { - dependencies.put(checkNotNull(testedProject), "*"); - testedProjects.add(checkNotNull(testedProject)); - return this; - } - - /** Sets the implementation id to the current builder. - * @param implementationId id for the implementations to choose from. - * @return the builder. - */ - def PackageJsonBuilder withImplementationId(String implementationId) { - this.implementationId = checkNotNull(implementationId) - return this; - } - - /** Adds a project to the list of implemented Projects. - * NOTE: also call {@link withImplementationId()} - * @param project id of the implemented API - * @return the builder. - */ - def PackageJsonBuilder withImplementedProject(String implementationAPI) { - implementedProjects.add(checkNotNull(implementationAPI)) - return this; - } - - /** Adds top-level property "workspaces" to the package.json, using an array with the given strings as value. */ - def PackageJsonBuilder withWorkspaces(String... workspaces) { - this.workspaces.addAll(workspaces); - return this; - } - - override toString() { - return '!!! This is just a preview of the N4JS package.json file !!!\n' + this.build(); - } - -}