diff --git a/apisupport.ant/nbproject/project.xml b/apisupport.ant/nbproject/project.xml --- a/apisupport.ant/nbproject/project.xml +++ b/apisupport.ant/nbproject/project.xml @@ -401,6 +401,14 @@ + org.openide.filesystems.templates + + + + 1.0 + + + org.openide.loaders diff --git a/cnd.debugger.gdb2/nbproject/project.properties b/cnd.debugger.gdb2/nbproject/project.properties --- a/cnd.debugger.gdb2/nbproject/project.properties +++ b/cnd.debugger.gdb2/nbproject/project.properties @@ -1,4 +1,4 @@ javac.source=1.6 javac.compilerargs=-Xlint -Xlint:-serial -spec.version.base=1.21.1 +spec.version.base=1.22.1 diff --git a/editor.document/src/org/netbeans/api/editor/document/LineDocumentUtils.java b/editor.document/src/org/netbeans/api/editor/document/LineDocumentUtils.java --- a/editor.document/src/org/netbeans/api/editor/document/LineDocumentUtils.java +++ b/editor.document/src/org/netbeans/api/editor/document/LineDocumentUtils.java @@ -522,7 +522,7 @@ T res = (T) serv; return res; } - + /** * Creates an empty document not attached to any environment, of the given * MIME type. The created document's type may differ for individual MIME types. @@ -535,7 +535,7 @@ public static @NonNull LineDocument createDocument(String mimeType) { DocumentFactory f = MimeLookup.getLookup(mimeType).lookup(DocumentFactory.class); if (f == null) { - throw new IllegalArgumentException("No document available for MIME type: " + mimeType); + throw new IllegalArgumentException("No document available for MIME type: " + mimeType); } Document doc = f.createDocument(mimeType); if (doc == null) { diff --git a/java.source/src/org/netbeans/modules/java/IndentFileEntry.java b/java.source/src/org/netbeans/modules/java/IndentFileEntry.java --- a/java.source/src/org/netbeans/modules/java/IndentFileEntry.java +++ b/java.source/src/org/netbeans/modules/java/IndentFileEntry.java @@ -115,6 +115,7 @@ /** Creates a new Java source from the template. Unlike the standard FileEntry.Format, this indents the resulting text using an indentation engine. */ + @Override public FileObject createFromTemplate (FileObject f, String name) throws IOException { String ext = getFile ().getExt (); diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties --- a/nbbuild/cluster.properties +++ b/nbbuild/cluster.properties @@ -261,6 +261,7 @@ openide.explorer,\ openide.filesystems.compat8,\ openide.filesystems.nb,\ + openide.filesystems.templates,\ openide.io,\ openide.loaders,\ openide.nodes,\ diff --git a/nbbuild/javadoctools/links.xml b/nbbuild/javadoctools/links.xml --- a/nbbuild/javadoctools/links.xml +++ b/nbbuild/javadoctools/links.xml @@ -234,3 +234,4 @@ + diff --git a/nbbuild/javadoctools/properties.xml b/nbbuild/javadoctools/properties.xml --- a/nbbuild/javadoctools/properties.xml +++ b/nbbuild/javadoctools/properties.xml @@ -233,3 +233,4 @@ + diff --git a/nbbuild/javadoctools/replaces.xml b/nbbuild/javadoctools/replaces.xml --- a/nbbuild/javadoctools/replaces.xml +++ b/nbbuild/javadoctools/replaces.xml @@ -233,3 +233,4 @@ + diff --git a/openide.filesystems.templates/arch.xml b/openide.filesystems.templates/arch.xml new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/arch.xml @@ -0,0 +1,298 @@ + + +]> + + + + &api-questions; + + +

+ API allows to create new files based on templates. Scripting engines can be specified for + processing the template, or custom Handlers may be registered to process certain templates. +

+

+ A template can use places substituable with parameter values; certain well-known parameters are + predefined, if the caller does not provide its custom values. +

+
+ +

+ The feature will be fully covered by unit tests. +

+
+ +

+ October 2014 +

+
+ + + + + +

+ The most important and only use-case is a file that is being created from a system-provided, or user-defined + boilerplate (template). +

+
+ +

+ This utility standardizes the process to use files as blueprints to create new files. +

+
+ + + + +

+ This module replaces some implementation in DataSystem APIs so the implementation + is usable even without DataSystems API itself. DataSystems API will use this + library. +

+
+ +

+ Yes. +

+
+ +

+ No. +

+
+ +

+ Yes. +

+
+ +

+ Requires JRE 7, for implementation reasons (AutoCloseable). +

+
+ +

+ JRE +

+
+ + + + +

+ None. +

+
+ +

+ No native platform dependencies. +

+
+ +

+ No specific deploy dependencies. +

+
+ +

+ JARs only. +

+
+ +

+ Yes. +

+
+ +

+ Yes, except API. +

+
+ +

+ Anywhere. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ Yes. +

+
+ +

+ None. +

+
+ +

+ None. +

+
+ +

+ None. +

+
+ +

+ Uses Lookup to locate SPIs to fill in template parameters and process templates. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No UI. +

+
+ +

+ Files are processed in-memory in documents; practical limits are imposed + by the platform's Document implementation. +

+
+ +

+ See 'perf-limit' +

+
+ +

+ No UI. +

+
+ +

+ No. +

+
+ +

+ XXX no answer for perf-scale +

+
+ +

+ No practical enforcement. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +

+ No. +

+
+ +
diff --git a/openide.filesystems.templates/build.xml b/openide.filesystems.templates/build.xml new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/build.xml @@ -0,0 +1,5 @@ + + + Builds, tests, and runs the project org.openide.filesystems.templates + + diff --git a/openide.filesystems.templates/manifest.mf b/openide.filesystems.templates/manifest.mf new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +AutoUpdate-Show-In-Client: true +OpenIDE-Module: org.openide.filesystems.templates +OpenIDE-Module-Localizing-Bundle: org/openide/filesystems/templates/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/openide.filesystems.templates/nbproject/project.properties b/openide.filesystems.templates/nbproject/project.properties new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/nbproject/project.properties @@ -0,0 +1,3 @@ +javac.source=1.7 +javac.compilerargs=-Xlint -Xlint:-serial +javadoc.arch=${basedir}/arch.xml diff --git a/openide.filesystems.templates/nbproject/project.xml b/openide.filesystems.templates/nbproject/project.xml new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/nbproject/project.xml @@ -0,0 +1,122 @@ + + + org.netbeans.modules.apisupport.project + + + org.openide.filesystems.templates + + + org.netbeans.api.annotations.common + + + + 1 + 1.25 + + + + org.netbeans.modules.editor.document + + + + 1.1 + + + + org.netbeans.modules.editor.indent + + + + 2 + 1.39 + + + + org.netbeans.modules.nbjunit + + + + 1 + 1.85 + + + + org.netbeans.modules.queries + + + + 1 + 1.40 + + + + org.openide.filesystems + + + + 9.1 + + + + org.openide.util.base + + + + 9.1 + + + + org.openide.util.lookup + + + + 8.26 + + + + + + unit + + org.netbeans.libs.freemarker + + + org.netbeans.modules.editor.mimelookup + + + + + org.netbeans.modules.editor.mimelookup.impl + + + org.netbeans.modules.masterfs + + + org.openide.awt + + + org.openide.dialogs + + + + org.openide.loaders + + + + org.openide.nodes + + + + org.openide.util.lookup + + + + + + + org.netbeans.api.templates + org.openide.loaders + + + + diff --git a/openide.loaders/src/org/openide/loaders/CreateFromTemplateAttributesProvider.java b/openide.filesystems.templates/src/org/netbeans/api/templates/CreateFromTemplateAttributes.java copy from openide.loaders/src/org/openide/loaders/CreateFromTemplateAttributesProvider.java copy to openide.filesystems.templates/src/org/netbeans/api/templates/CreateFromTemplateAttributes.java --- a/openide.loaders/src/org/openide/loaders/CreateFromTemplateAttributesProvider.java +++ b/openide.filesystems.templates/src/org/netbeans/api/templates/CreateFromTemplateAttributes.java @@ -32,20 +32,22 @@ * Portions Copyrighted 2007 Sun Microsystems, Inc. */ -package org.openide.loaders; +package org.netbeans.api.templates; import java.util.Map; +import org.openide.filesystems.FileObject; /** This is an interface for smart templating. * Implementations of this class can be registered in the global {@link org.openide.util.Lookup} * and allows anyone provide additional parameters to each {@link CreateFromTemplateHandler}s * when a template is instantiating. * Read more in the howto document. - * + *

+ * This interface supersedes {@code CreateFromTemplateAttributesProvider} in {@code openide.loaders} module. * @author Jaroslav Tulach * @since 6.3 */ -public interface CreateFromTemplateAttributesProvider { +public interface CreateFromTemplateAttributes { /** Called when a template is about to be instantiated to provide additional * values to the {@link CreateFromTemplateHandler} that will handle the * template instantiation. @@ -55,5 +57,5 @@ * @param name the name of the object to create * @return map of named objects, or null */ - Map attributesFor(DataObject template, DataFolder target, String name); + Map attributesFor(FileObject template, FileObject target, String name); } diff --git a/openide.loaders/src/org/openide/loaders/CreateFromTemplateHandler.java b/openide.filesystems.templates/src/org/netbeans/api/templates/CreateFromTemplateHandler.java copy from openide.loaders/src/org/openide/loaders/CreateFromTemplateHandler.java copy to openide.filesystems.templates/src/org/netbeans/api/templates/CreateFromTemplateHandler.java --- a/openide.loaders/src/org/openide/loaders/CreateFromTemplateHandler.java +++ b/openide.filesystems.templates/src/org/netbeans/api/templates/CreateFromTemplateHandler.java @@ -32,7 +32,7 @@ * Portions Copyrighted 2007 Sun Microsystems, Inc. */ -package org.openide.loaders; +package org.netbeans.api.templates; import java.io.IOException; import java.util.Map; diff --git a/openide.loaders/src/org/netbeans/api/templates/TemplateRegistration.java b/openide.filesystems.templates/src/org/netbeans/api/templates/TemplateRegistration.java rename from openide.loaders/src/org/netbeans/api/templates/TemplateRegistration.java rename to openide.filesystems.templates/src/org/netbeans/api/templates/TemplateRegistration.java --- a/openide.loaders/src/org/netbeans/api/templates/TemplateRegistration.java +++ b/openide.filesystems.templates/src/org/netbeans/api/templates/TemplateRegistration.java @@ -43,8 +43,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.script.ScriptEngineFactory; -import org.openide.WizardDescriptor.InstantiatingIterator; -import org.openide.loaders.TemplateWizard; /** * Registers a template the user can select. diff --git a/openide.loaders/src/org/netbeans/api/templates/TemplateRegistrations.java b/openide.filesystems.templates/src/org/netbeans/api/templates/TemplateRegistrations.java rename from openide.loaders/src/org/netbeans/api/templates/TemplateRegistrations.java rename to openide.filesystems.templates/src/org/netbeans/api/templates/TemplateRegistrations.java diff --git a/openide.filesystems.templates/src/org/netbeans/api/templates/TemplateUtils.java b/openide.filesystems.templates/src/org/netbeans/api/templates/TemplateUtils.java new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/src/org/netbeans/api/templates/TemplateUtils.java @@ -0,0 +1,375 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2014 Sun Microsystems, Inc. + */ +package org.netbeans.api.templates; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.Charset; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.queries.FileEncodingQuery; +import org.netbeans.modules.templates.IndentWriter; +import org.netbeans.modules.templates.ScriptingCreateFromTemplateHandler; +import org.openide.filesystems.FileLock; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.util.*; + +/** + * Provides utilities for processing file templates. + * + * @author sdedic + */ +public class TemplateUtils { + + /** + * Populates default values for template parameters. Each template can have its specific parameters and their default values + * are defined by {@link CreateFromTemplateAttributes} SPI registered in the Lookup. The 'param' provides user-defined values, + * which always take precedence over the defaults. + *

+ * Certain values are always filled in, if not specified: + *

    + *
  • {@code user} the invoking user name + *
  • {@code date} system date : String + *
  • {@code time} system time : String + *
  • {@code dateTime} java.util.Date representation of the current system time + *
+ * + * @param template + * @param folder + * @param name + * @param param + * @return completed parameters + */ + public static Map findTemplateParameters(FileObject template, FileObject folder, String name, Map param) { + HashMap all = new HashMap(); + for (CreateFromTemplateAttributes provider : Lookup.getDefault().lookupAll(CreateFromTemplateAttributes.class)) { + Map map = provider.attributesFor(template, folder, name); + if (map != null) { + for (Map.Entry e : map.entrySet()) { + all.put(e.getKey(), e.getValue()); + } + } + } + if (param != null) { + for (Map.Entry e : param.entrySet()) { + all.put(e.getKey(), e.getValue()); + } + } + + if (!all.containsKey("name") && name != null) { // NOI18N + if (Boolean.TRUE.equals(all.get(CreateFromTemplateHandler.FREE_FILE_EXTENSION))) { + name = name.replaceFirst("[.].*", ""); + } + all.put("name", name); // NOI18N + } + if (!all.containsKey("user")) { // NOI18N + all.put("user", UserInformation.getInfo().getLogin()); // NOI18N + } + Date d = new Date(); + if (!all.containsKey("date")) { // NOI18N + all.put("date", DateFormat.getDateInstance(DateFormat.DEFAULT, UserInformation.getInfo().getLocale()).format(d)); // NOI18N + } + if (!all.containsKey("time")) { // NOI18N + all.put("time", DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, UserInformation.getInfo().getLocale()).format(d)); // NOI18N + } + if (!all.containsKey("dateTime")) { // NOI18N + all.put("dateTime", d); // NOI18N + } + + return Collections.unmodifiableMap(all); + } + + private static Map enhanceParameters(Map old, String name, String ext) { + HashMap all = new HashMap(old); + if (!all.containsKey("nameAndExt") && name != null) { // NOI18N + if (ext != null && ext.length() > 0 && + (!Boolean.TRUE.equals(old.get(CreateFromTemplateHandler.FREE_FILE_EXTENSION)) || name.indexOf('.') == -1)) { + all.put("nameAndExt", name + '.' + ext); // NOI18N + } else { + all.put("nameAndExt", name); // NOI18N + } + } + return Collections.unmodifiableMap(all); + } + + private static final String PROP_TEMPLATE = "template"; // NOI18N + private static final String EA_PREFORMATTED = "org-netbeans-modules-java-preformattedSource"; // NOI18N + private static final String NEWLINE = "\n"; // NOI18N + + /** + * Determines the default procedure for copying the template in {@link #createFromTemplate}. + */ + public static enum Mode { + /** + * The template will be formatted using formatter. + */ + FORMAT, + /** + * The template will be just copied. + */ + COPY, + /** + * The template will not be processed if no custom {@link CreateFromTemplateHandler} handles it. + */ + FAIL + } + + /** + * Creates a new file based on the template. The method will collect parameters + * tied to the template using registered {@link CreateFromTemplateAttributes} providers, + * and will try to locate a willing {@link CreateFromTemplateHandler} that will create + * the target file. If no such handler exists, and the {@code defaultCopy} parameter is true, + * the file contents is just copied to the target location. + *

+ * If the {@code name} parameter is null, the function attempts to compute a suitable name + * from the file. + *

+ * The default copy algorithm uses the supplied {@code format} to process tokens. A standard + * format (using __TOKEN__) can be obtained by calling {@link #basicFormatter}. + *

+ * If the passed {@code name} is {@code null}, the implementation will pick a free name based on + * the template's own name (see {@link FileUtil#findFreeFileName}). + * @param f the template file + * @param folder the target folder, must exist + * @param name the desired name. If {@code null}, the implementation will choose the name. + * @param attributes values to apply on the template. May be {@code null} = no values. + * @return The created file, or {@code null} if no creation handler is located. + * @throws IOException + */ + @SuppressWarnings("AssignmentToMethodParameter") + @CheckForNull + public static FileObject createFromTemplate(@NonNull FileObject f, @NonNull FileObject folder, + @NullAllowed String name, @NullAllowed Map attributes, + Mode defaultMode) + throws IOException { + Format frm = null; + + switch (defaultMode) { + case FORMAT: + MapFormat mf = new MapFormat(new HashMap()); + mf.setExactMatch(false); + mf.setLeftBrace("__"); + mf.setRightBrace("__"); + frm = mf; + break; + + case COPY: + frm = new Format() { + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + toAppendTo.append(obj); + return toAppendTo; + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + }; + break; + } + return createFromTemplate(f, folder, name, attributes, defaultMode, + frm); + } + + /** + * Creates a new file based on the template. For the detailed description, + * please see {@link #createFromTemplate(org.openide.filesystems.FileObject, org.openide.filesystems.FileObject, java.lang.String, java.util.Map, org.netbeans.api.templates.TemplateUtils.Mode)}. + * This variant allows the caller to supply own {@link Format} object that will be used + * for default processing. The {@code defaultMode} parameter from the {@code createFromTemplate} mentioned + * above is fixed to {@link Mode#FORMAT}. + *

+ * If the supplied {@code format} extends {@link MapFormat}, collected and standard + * attributes will be added to its value map returned by the {@link MapFormat#getMap} instance. + * + * @param f the template file + * @param folder the target folder + * @param name the desired name, {@code null} for automatic name + * @param attributes parameters applied to the template + * @param frm the custom format, possibly configured with parameters + * @return created file + * @throws IOException if the creation fails + */ + public static FileObject createFromTemplate(@NonNull FileObject f, @NonNull FileObject folder, + @NullAllowed String name, @NullAllowed Map attributes, + Format frm) + throws IOException { + assert frm != null; + return createFromTemplate(f, folder, name, attributes, Mode.FORMAT, + frm); + } + + @SuppressWarnings("AssignmentToMethodParameter") + @CheckForNull + private static FileObject createFromTemplate(@NonNull FileObject f, @NonNull FileObject folder, + @NullAllowed String name, @NullAllowed Map attributes, + Mode defaultMode, Format frm) + throws IOException { + Parameters.notNull("f", f); + Parameters.notNull("folder", folder); + assert defaultMode != Mode.FORMAT || frm != null : "Format must be provided for Mode.FORMAT"; + String ext = f.getExt(); + + if (!folder.isFolder()) { + throw new IllegalArgumentException("Not a folder: " + folder); + } + if (name == null) { + name = FileUtil.findFreeFileName( + folder, f.getName (), f.getExt () + ); + } + + FileObject pf = null; + Map params = null; + for (CreateFromTemplateHandler h : Lookup.getDefault().lookupAll(CreateFromTemplateHandler.class)) { + if (h.accept(f)) { + if (params == null) { + params = findTemplateParameters(f, folder, name, attributes); + } + pf = h.createFromTemplate(f, folder, name, + enhanceParameters(params, name, f.getExt()) + ); + assert pf != null; + break; + } + } + if (pf != null || defaultMode == Mode.FAIL) { + return pf; + } + if (params == null) { + params = attributes == null ? Collections. emptyMap() : + (Map)attributes; + } + if (defaultMode != Mode.COPY && frm instanceof MapFormat) { + MapFormat mf = (MapFormat)frm; + Map m = mf.getMap(); + for (String s: params.keySet()) { + if (m.containsKey(s)) { + continue; + } + m.put(s, params.get(s)); + } + } + + FileObject fo = folder.createData (name, ext); + boolean preformatted = false; + Charset encoding = FileEncodingQuery.getEncoding(f); + boolean success = false; + + FileLock lock = fo.lock (); + try (InputStream is= f.getInputStream (); + Reader reader = new InputStreamReader(is,encoding); + BufferedReader r = new BufferedReader (reader)) { + + Object attr = f.getAttribute(EA_PREFORMATTED); + if (attr != null && attr instanceof Boolean) { + preformatted = ((Boolean)attr); + } + + encoding = FileEncodingQuery.getEncoding(fo); + + Document doc = ScriptingCreateFromTemplateHandler.createDocument(f.getMIMEType()); + + // PENDING: originally, preformatted meant that only changed + // lines were formatted. Now preformatted is not formatted at all + try ( + OutputStream os=fo.getOutputStream(lock); + OutputStreamWriter w = new OutputStreamWriter(os, encoding); + Writer iw = preformatted ? w : new IndentWriter(doc, 0, w, true)) { + + String line = null; + String current; + + while ((current = r.readLine ()) != null) { + if (line != null) { + // newline between lines + iw.append(NEWLINE); + } + if (frm != null) { + line = frm.format (current); + } else { + line = current; + } + iw.append(line); + } + iw.append(NEWLINE); + iw.flush(); + } + // copy attributes + // hack to overcome package-private modifier in setTemplate(fo, boolean) + FileUtil.copyAttributes(f, fo); + fo.setAttribute(PROP_TEMPLATE, null); + success = true; + } catch (IOException ex) { + try { + fo.delete(lock); + } catch (IOException ex2) { + throw ex; + } + } finally { + if (!success) { + // try to delete the malformed file: + fo.delete(lock); + } + lock.releaseLock(); + } + return fo; + } + +} diff --git a/openide.loaders/src/org/netbeans/modules/templates/Bundle.properties b/openide.filesystems.templates/src/org/netbeans/modules/templates/Bundle.properties copy from openide.loaders/src/org/netbeans/modules/templates/Bundle.properties copy to openide.filesystems.templates/src/org/netbeans/modules/templates/Bundle.properties diff --git a/openide.filesystems.templates/src/org/netbeans/modules/templates/IndentWriter.java b/openide.filesystems.templates/src/org/netbeans/modules/templates/IndentWriter.java new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/src/org/netbeans/modules/templates/IndentWriter.java @@ -0,0 +1,126 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2014 Sun Microsystems, Inc. + */ +package org.netbeans.modules.templates; + +import java.io.IOException; +import java.io.Writer; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.Position; +import org.netbeans.modules.editor.indent.api.Reformat; + +/** + * + * @author sdedic + */ +public final class IndentWriter extends Writer { + private final Document doc; + private int offset; + private final Writer writer; + private final StringBuilder buffer; + private int writtenLen = 0; + private final boolean saneFlush; + + public IndentWriter(Document doc, int offset, Writer writer, boolean saneFlush) { + if (offset < 0) { + throw new IllegalArgumentException("offset=" + offset + " < 0"); // NOI18N + } + if (offset > doc.getLength()) { + throw new IllegalArgumentException("offset=" + offset + " > docLen=" + doc.getLength()); // NOI18N + } + this.saneFlush = saneFlush; + this.doc = doc; + this.offset = offset; + this.writer = writer; + this.buffer = new StringBuilder(); + } + + @Override + public void write(int c) throws IOException { + write(new char[]{(char) c}, 0, 1); + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + // Add the chars to the buffer for formatting + buffer.append(cbuf, off, len); + } + + /** + * Flush is called mulitple times by e.g. scripting engine. + * @throws IOException + */ + @Override + public void flush() throws IOException { + if (saneFlush) { + finalFlush(); + } + } + + public void finalFlush() throws IOException { + Reformat reformat = Reformat.get(doc); + reformat.lock(); + try { + String text = buffer.toString(); + if (text.length() > 0 && offset <= doc.getLength()) { + try { + doc.insertString(offset, text, null); + Position endPos = doc.createPosition(offset + text.length()); + reformat.reformat(offset, endPos.getOffset()); + int len = endPos.getOffset() - offset; + String reformattedText = doc.getText(offset, len); + doc.remove(offset, len); + writer.write(reformattedText.substring(writtenLen)); + writtenLen = len; + } catch (BadLocationException e) { + } + } + } finally { + reformat.unlock(); + } + } + + @Override + public void close() throws IOException { + flush(); + } +} diff --git a/openide.loaders/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateHandler.java b/openide.filesystems.templates/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateHandler.java rename from openide.loaders/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateHandler.java rename to openide.filesystems.templates/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateHandler.java --- a/openide.loaders/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateHandler.java +++ b/openide.filesystems.templates/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateHandler.java @@ -47,12 +47,13 @@ import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; +import javax.swing.text.Document; import javax.swing.text.PlainDocument; +import org.netbeans.api.editor.document.LineDocumentUtils; import org.netbeans.api.queries.FileEncodingQuery; +import org.netbeans.api.templates.CreateFromTemplateHandler; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; -import org.openide.loaders.CreateFromTemplateHandler; -import org.openide.text.IndentEngine; import org.openide.util.Lookup; import org.openide.util.lookup.ServiceProvider; @@ -72,12 +73,12 @@ private static final String ENCODING_PROPERTY_NAME = "encoding"; //NOI18N @Override - protected boolean accept(FileObject orig) { + public boolean accept(FileObject orig) { return engine(orig) != null; } @Override - protected FileObject createFromTemplate(FileObject template, FileObject f, + public FileObject createFromTemplate(FileObject template, FileObject f, String name, Map values) throws IOException { boolean noExt = Boolean.TRUE.equals(values.get(FREE_FILE_EXTENSION)) && name.indexOf('.') != -1; @@ -106,31 +107,19 @@ bind.put(ENCODING_PROPERTY_NAME, targetEnc.name()); } - Writer w = null; - Reader is = null; - try { - w = new OutputStreamWriter(output.getOutputStream(), targetEnc); - - IndentEngine format = IndentEngine.find(template.getMIMEType()); - if (format != null) { - PlainDocument doc = new PlainDocument(); - doc.putProperty(PlainDocument.StreamDescriptionProperty, template); - w = format.createWriter(doc, 0, w); - } - - - eng.getContext().setWriter(new PrintWriter(w)); + Document doc = createDocument(template.getMIMEType()); + try (Writer w = new OutputStreamWriter(output.getOutputStream(), targetEnc); + Reader is = new InputStreamReader(template.getInputStream(), sourceEnc); + IndentWriter w2 = new IndentWriter(doc, 0, w, false)) { + eng.getContext().setWriter(new PrintWriter(w2)); //eng.getContext().setBindings(bind, ScriptContext.ENGINE_SCOPE); eng.getContext().setAttribute(FileObject.class.getName(), template, ScriptContext.ENGINE_SCOPE); eng.getContext().setAttribute(ScriptEngine.FILENAME, template.getNameExt(), ScriptContext.ENGINE_SCOPE); - is = new InputStreamReader(template.getInputStream(), sourceEnc); eng.eval(is); + w2.finalFlush(); }catch (ScriptException ex) { IOException io = new IOException(ex.getMessage(), ex); throw io; - } finally { - if (w != null) w.close(); - if (is != null) is.close(); } return output; } @@ -151,4 +140,16 @@ } return null; } + + public static Document createDocument(String mimeType) { + Document doc; + try { + doc = LineDocumentUtils.createDocument(mimeType); + } catch (IllegalArgumentException ex) { + // mainly for tests + doc = new PlainDocument(); + doc.putProperty("mimeType", mimeType); + } + return doc; + } } diff --git a/openide.loaders/src/org/netbeans/modules/templates/TemplateProcessor.java b/openide.filesystems.templates/src/org/netbeans/modules/templates/TemplateProcessor2.java copy from openide.loaders/src/org/netbeans/modules/templates/TemplateProcessor.java copy to openide.filesystems.templates/src/org/netbeans/modules/templates/TemplateProcessor2.java --- a/openide.loaders/src/org/netbeans/modules/templates/TemplateProcessor.java +++ b/openide.filesystems.templates/src/org/netbeans/modules/templates/TemplateProcessor2.java @@ -53,14 +53,18 @@ import org.openide.filesystems.annotations.LayerGenerationException; import org.netbeans.api.templates.TemplateRegistration; import org.netbeans.api.templates.TemplateRegistrations; -import org.openide.WizardDescriptor.InstantiatingIterator; import org.openide.filesystems.annotations.LayerBuilder; import org.openide.filesystems.annotations.LayerGeneratingProcessor; import org.openide.util.lookup.ServiceProvider; +/** + * The implementation has moved from data systems. Part of the implementation is still there, + * as the {@code InstantiatingIterator} support requires + * @author sdedic + */ @ServiceProvider(service=Processor.class) @SupportedSourceVersion(SourceVersion.RELEASE_7) -public class TemplateProcessor extends LayerGeneratingProcessor { +public class TemplateProcessor2 extends LayerGeneratingProcessor { @Override public Set getSupportedAnnotationTypes() { return new HashSet(Arrays.asList(TemplateRegistration.class.getCanonicalName(), TemplateRegistrations.class.getCanonicalName())); @@ -86,7 +90,7 @@ process(e, t); } } - return true; + return false; } private void process(Element e, TemplateRegistration t) throws LayerGenerationException { @@ -124,9 +128,9 @@ if (!t.description().isEmpty()) { f.urlvalue("instantiatingWizardURL", contentURI(e, t.description(), builder, t, "description")); } - if (e.getKind() != ElementKind.PACKAGE) { - f.instanceAttribute("instantiatingIterator", InstantiatingIterator.class); - } +// if (e.getKind() != ElementKind.PACKAGE) { +// f.instanceAttribute("instantiatingIterator", InstantiatingIterator.class); +// } if (t.content().length > 0) { f.url(contentURI(e, t.content()[0], builder, t, "content").toString()); for (int i = 1; i < t.content().length; i++) { diff --git a/openide.filesystems.templates/src/org/openide/filesystems/templates/Bundle.properties b/openide.filesystems.templates/src/org/openide/filesystems/templates/Bundle.properties new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/src/org/openide/filesystems/templates/Bundle.properties @@ -0,0 +1,1 @@ +OpenIDE-Module-Name=File Templates diff --git a/openide.filesystems.templates/test/unit/data/golden/ClassWithoutReplacements.java b/openide.filesystems.templates/test/unit/data/golden/ClassWithoutReplacements.java new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/test/unit/data/golden/ClassWithoutReplacements.java @@ -0,0 +1,7 @@ +/** + * + * @author sdedic + */ +public class Templateclass1 { + +} diff --git a/openide.filesystems.templates/test/unit/data/golden/ForceNoReplacements.java b/openide.filesystems.templates/test/unit/data/golden/ForceNoReplacements.java new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/test/unit/data/golden/ForceNoReplacements.java @@ -0,0 +1,7 @@ +/** + * + * @author __USER__ + */ +public class Templateclass1 { + +} diff --git a/java.source/src/org/netbeans/modules/java/source/resources/GeneratedMethodBody.template b/openide.filesystems.templates/test/unit/data/golden/GeneratedMethodBody.java copy from java.source/src/org/netbeans/modules/java/source/resources/GeneratedMethodBody.template copy to openide.filesystems.templates/test/unit/data/golden/GeneratedMethodBody.java --- a/java.source/src/org/netbeans/modules/java/source/resources/GeneratedMethodBody.template +++ b/openide.filesystems.templates/test/unit/data/golden/GeneratedMethodBody.java @@ -1,13 +1,2 @@ -<#-- -A built-in Freemarker template (see http://freemarker.sourceforge.net) used for -filling the body of methods generated by the IDE. When editing the template, -the following predefined variables, that will be then expanded into -the corresponding values, could be used together with Java expressions and -comments: -${method_return_type} a return type of a created method -${default_return_value} a value returned by the method by default -${method_name} name of the created method -${class_name} qualified name of the enclosing class -${simple_class_name} simple name of the enclosing class ---> -throw new java.lang.UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. +return 42; + diff --git a/java.source/src/org/netbeans/modules/java/source/resources/GeneratedMethodBody.template b/openide.filesystems.templates/test/unit/data/golden/GeneratedMethodBody2.java copy from java.source/src/org/netbeans/modules/java/source/resources/GeneratedMethodBody.template copy to openide.filesystems.templates/test/unit/data/golden/GeneratedMethodBody2.java --- a/java.source/src/org/netbeans/modules/java/source/resources/GeneratedMethodBody.template +++ b/openide.filesystems.templates/test/unit/data/golden/GeneratedMethodBody2.java @@ -1,13 +1,2 @@ -<#-- -A built-in Freemarker template (see http://freemarker.sourceforge.net) used for -filling the body of methods generated by the IDE. When editing the template, -the following predefined variables, that will be then expanded into -the corresponding values, could be used together with Java expressions and -comments: -${method_return_type} a return type of a created method -${default_return_value} a value returned by the method by default -${method_name} name of the created method -${class_name} qualified name of the enclosing class -${simple_class_name} simple name of the enclosing class ---> -throw new java.lang.UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. +return 24; + diff --git a/openide.filesystems.templates/test/unit/data/golden/SimpleReplacements.java b/openide.filesystems.templates/test/unit/data/golden/SimpleReplacements.java new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/test/unit/data/golden/SimpleReplacements.java @@ -0,0 +1,7 @@ +/** + * + * @author foobar + */ +public class Templateclass1 { + +} diff --git a/openide.filesystems.templates/test/unit/data/templates/ClassWithoutReplacements.java b/openide.filesystems.templates/test/unit/data/templates/ClassWithoutReplacements.java new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/test/unit/data/templates/ClassWithoutReplacements.java @@ -0,0 +1,7 @@ +/** + * + * @author sdedic + */ +public class Templateclass1 { + +} diff --git a/java.source/src/org/netbeans/modules/java/source/resources/GeneratedMethodBody.template b/openide.filesystems.templates/test/unit/data/templates/GeneratedMethodBody.java copy from java.source/src/org/netbeans/modules/java/source/resources/GeneratedMethodBody.template copy to openide.filesystems.templates/test/unit/data/templates/GeneratedMethodBody.java --- a/java.source/src/org/netbeans/modules/java/source/resources/GeneratedMethodBody.template +++ b/openide.filesystems.templates/test/unit/data/templates/GeneratedMethodBody.java @@ -10,4 +10,5 @@ ${class_name} qualified name of the enclosing class ${simple_class_name} simple name of the enclosing class --> -throw new java.lang.UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. +return ${default_return_value}; + diff --git a/openide.filesystems.templates/test/unit/data/templates/SimpleReplacements.java b/openide.filesystems.templates/test/unit/data/templates/SimpleReplacements.java new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/test/unit/data/templates/SimpleReplacements.java @@ -0,0 +1,7 @@ +/** + * + * @author __USER__ + */ +public class Templateclass1 { + +} diff --git a/openide.filesystems.templates/test/unit/src/org/netbeans/api/templates/TemplateUtilsTest.java b/openide.filesystems.templates/test/unit/src/org/netbeans/api/templates/TemplateUtilsTest.java new file mode 100644 --- /dev/null +++ b/openide.filesystems.templates/test/unit/src/org/netbeans/api/templates/TemplateUtilsTest.java @@ -0,0 +1,175 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2014 Sun Microsystems, Inc. + */ +package org.netbeans.api.templates; + +import java.util.HashMap; +import java.util.Map; +import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.templates.ScriptingCreateFromTemplateHandler; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.util.lookup.Lookups; +import org.openide.util.test.MockLookup; + +/** + * + * @author sdedic + */ +public class TemplateUtilsTest extends NbTestCase { + + public TemplateUtilsTest(String name) { + super(name); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + clearWorkDir(); + } + + /** + * Checks creation of templates without any special processing. + * @throws Exception + */ + public void testCreatePlain() throws Exception { + FileObject dataRoot = FileUtil.toFileObject(getDataDir()); + FileObject template = dataRoot.getFileObject("templates/ClassWithoutReplacements.java"); + template.setAttribute("template", Boolean.TRUE); + FileObject workRoot = FileUtil.toFileObject(getWorkDir()); + FileObject result = TemplateUtils.createFromTemplate(template, workRoot, "NoReplacements", null, TemplateUtils.Mode.FORMAT); + FileObject pass = dataRoot.getFileObject("golden/ClassWithoutReplacements.java"); + assertFile(FileUtil.toFile(result), FileUtil.toFile(pass)); + } + + /** + * Forces plain default processing, although the template contains replaceable parts + */ + public void testCreateForcePlain() throws Exception { + FileObject dataRoot = FileUtil.toFileObject(getDataDir()); + FileObject template = dataRoot.getFileObject("templates/SimpleReplacements.java"); + template.setAttribute("template", Boolean.TRUE); + FileObject workRoot = FileUtil.toFileObject(getWorkDir()); + Map m = new HashMap(); + m.put("USER", "foobar"); + FileObject result = TemplateUtils.createFromTemplate(template, workRoot, "NoReplacements", m, TemplateUtils.Mode.COPY); + FileObject pass = dataRoot.getFileObject("golden/ForceNoReplacements.java"); + assertFile(FileUtil.toFile(result), FileUtil.toFile(pass)); + } + + /** + * Uses a simple format, this is the mode applied by former implementation in MultiDataObject + */ + public void testCreateReplaceSimple() throws Exception { + clearWorkDir(); + FileObject dataRoot = FileUtil.toFileObject(getDataDir()); + FileObject template = dataRoot.getFileObject("templates/SimpleReplacements.java"); + template.setAttribute("template", Boolean.TRUE); + FileObject workRoot = FileUtil.toFileObject(getWorkDir()); + Map m = new HashMap(); + m.put("USER", "foobar"); + FileObject result = TemplateUtils.createFromTemplate(template, workRoot, "SimpleReplacements", m, TemplateUtils.Mode.FORMAT); + FileObject pass = dataRoot.getFileObject("golden/SimpleReplacements.java"); + assertFile(FileUtil.toFile(result), FileUtil.toFile(pass)); + } + + public void testScriptingTemplate() throws Exception { + clearWorkDir(); + FileObject dataRoot = FileUtil.toFileObject(getDataDir()); + FileObject template = dataRoot.getFileObject("templates/GeneratedMethodBody.java"); + template.setAttribute("template", Boolean.TRUE); + template.setAttribute(ScriptingCreateFromTemplateHandler.SCRIPT_ENGINE_ATTR, "freemarker"); + FileObject workRoot = FileUtil.toFileObject(getWorkDir()); + Map m = new HashMap(); + m.put("default_return_value", "42"); + FileObject result = TemplateUtils.createFromTemplate(template, workRoot, "GeneratedMethodBody", m, TemplateUtils.Mode.FORMAT); + FileObject pass = dataRoot.getFileObject("golden/GeneratedMethodBody.java"); + assertFile(FileUtil.toFile(result), FileUtil.toFile(pass)); + } + + class DefaultValueAttribute implements CreateFromTemplateAttributes { + @Override + public Map attributesFor(FileObject template, FileObject target, String name) { + FileObject dataRoot = FileUtil.toFileObject(getDataDir()); + FileObject t = dataRoot.getFileObject("templates/GeneratedMethodBody.java"); + if (t != template) { + return null; + } + + Map m = new HashMap(); + m.put("default_return_value", "42"); + + return m; + } + } + + public void testAddTemplateParameters() throws Exception { + MockLookup.setLookup( + Lookups.metaInfServices(getClass().getClassLoader()), + Lookups.fixed(new DefaultValueAttribute())); + clearWorkDir(); + FileObject dataRoot = FileUtil.toFileObject(getDataDir()); + FileObject template = dataRoot.getFileObject("templates/GeneratedMethodBody.java"); + template.setAttribute("template", Boolean.TRUE); + template.setAttribute(ScriptingCreateFromTemplateHandler.SCRIPT_ENGINE_ATTR, "freemarker"); + FileObject workRoot = FileUtil.toFileObject(getWorkDir()); + FileObject result = TemplateUtils.createFromTemplate(template, workRoot, "GeneratedMethodBody", null, TemplateUtils.Mode.FORMAT); + FileObject pass = dataRoot.getFileObject("golden/GeneratedMethodBody.java"); + assertFile(FileUtil.toFile(result), FileUtil.toFile(pass)); + } + + public void testOverridenParameters() throws Exception { + MockLookup.setLookup( + Lookups.metaInfServices(getClass().getClassLoader()), + Lookups.fixed(new DefaultValueAttribute())); + clearWorkDir(); + FileObject dataRoot = FileUtil.toFileObject(getDataDir()); + FileObject template = dataRoot.getFileObject("templates/GeneratedMethodBody.java"); + template.setAttribute("template", Boolean.TRUE); + template.setAttribute(ScriptingCreateFromTemplateHandler.SCRIPT_ENGINE_ATTR, "freemarker"); + FileObject workRoot = FileUtil.toFileObject(getWorkDir()); + Map m = new HashMap(); + m.put("default_return_value", "24"); + FileObject result = TemplateUtils.createFromTemplate(template, workRoot, "GeneratedMethodBody", m, TemplateUtils.Mode.FORMAT); + FileObject pass = dataRoot.getFileObject("golden/GeneratedMethodBody2.java"); + assertFile(FileUtil.toFile(result), FileUtil.toFile(pass)); + } +} diff --git a/openide.loaders/test/unit/src/org/netbeans/modules/templates/Bug138973Test.java b/openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/Bug138973Test.java rename from openide.loaders/test/unit/src/org/netbeans/modules/templates/Bug138973Test.java rename to openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/Bug138973Test.java --- a/openide.loaders/test/unit/src/org/netbeans/modules/templates/Bug138973Test.java +++ b/openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/Bug138973Test.java @@ -65,6 +65,7 @@ import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.openide.loaders.DataObjectEncodingQueryImplementation; +import org.netbeans.modules.templates.ScriptingCreateFromTemplateHandler; import org.netbeans.spi.queries.FileEncodingQueryImplementation; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; @@ -125,11 +126,11 @@ public static final class SimpleTemplateHandler extends CreateFromTemplateHandler { @Override - protected boolean accept(FileObject orig) { + public boolean accept(FileObject orig) { return true; } @Override - protected FileObject createFromTemplate(FileObject template, + public FileObject createFromTemplate(FileObject template, FileObject targetFolder, String name, Map parameters) throws IOException { diff --git a/openide.loaders/test/unit/src/org/openide/loaders/CreateFromTemplateHandlerTest.java b/openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/CreateFromTemplateHandlerTest.java copy from openide.loaders/test/unit/src/org/openide/loaders/CreateFromTemplateHandlerTest.java copy to openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/CreateFromTemplateHandlerTest.java --- a/openide.loaders/test/unit/src/org/openide/loaders/CreateFromTemplateHandlerTest.java +++ b/openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/CreateFromTemplateHandlerTest.java @@ -32,7 +32,7 @@ * Portions Copyrighted 2007 Sun Microsystems, Inc. */ -package org.openide.loaders; +package org.netbeans.modules.templates; import java.io.IOException; import java.util.ArrayList; @@ -41,10 +41,20 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.netbeans.api.templates.CreateFromTemplateAttributes; import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; +import org.openide.loaders.CreateFromTemplateHandler; +import org.openide.loaders.DataFolder; +import org.openide.loaders.DataLoader; +import org.openide.loaders.DataLoaderPool; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObjectExistsException; +import org.openide.loaders.FileEntry; +import org.openide.loaders.MultiDataObject; +import org.openide.loaders.MultiFileLoader; import org.openide.util.Enumerations; /** @@ -107,110 +117,17 @@ fail("Modifications shall be unsupported"); } - public void testTemplateWizardCopiesItsPropertiesToMap() throws Exception { - doTemplateWizardCopiesItsPropertiesToMap("simpleObject.txt"); - } - - public void testTemplateWizardCopiesItsPropertiesToMapForOverridenEntry() throws Exception { - DataObject obj = doTemplateWizardCopiesItsPropertiesToMap("simpleObject.prima"); - assertEquals("The right loader", SimpleLoader.class, obj.getLoader().getClass()); - } - - public void testTemplateWizardCopiesItsPropertiesToMapForOverridenEntryOnMoreEntries() throws Exception { - FileObject root = FileUtil.createMemoryFileSystem().getRoot(); - FileObject fo = FileUtil.createData(root, "simpleObject.java"); - FileObject fo2 = FileUtil.createData(root, "simpleObject.form"); - - DataObject obj = DataObject.find(fo); - - DataFolder folder = DataFolder.findFolder(FileUtil.createFolder(root, "target")); - - Map parameters = Collections.singletonMap("type", "empty"); - DataObject n = obj.createFromTemplate(folder, "complex", parameters); - - assertEquals("Created in right place", folder, n.getFolder()); - assertEquals("Created with right name", "complex", n.getName()); - - assertEquals("The right source1", fo, Hand.origObject.get(0)); - assertEquals("The right source2", fo2, Hand.origObject.get(1)); - assertEquals("The right source in query", fo, Hand.acceptObject.get(0)); - assertEquals("The right source in query2", fo2, Hand.acceptObject.get(1)); - assertEquals("The right destiny folder", folder.getPrimaryFile(), Hand.fileObject.get(0)); - assertEquals("The right destiny folder", folder.getPrimaryFile(), Hand.fileObject.get(1)); - assertEquals("The right name", "complex", Hand.name); - if (Hand.parameters.size() < 2) { - fail("As least two: " + Hand.parameters + " but was " + Hand.parameters.size()); - } - assertEquals("empty", Hand.parameters.get("type")); - assertEquals("complex", Hand.parameters.get("name")); - try { - Hand.parameters.put("kuk", "buk"); - } catch (UnsupportedOperationException ex) { - // ok - return; - } - fail("Modifications shall be unsupported"); - } - - private DataObject doTemplateWizardCopiesItsPropertiesToMap(String... fileName) throws Exception { - FileObject root = FileUtil.createMemoryFileSystem().getRoot(); - FileObject fo = null; - for (String fn : fileName) { - fo = FileUtil.createData(root, fn); - } - - DataObject obj = DataObject.find(fo); - - DataFolder folder = DataFolder.findFolder(FileUtil.createFolder(root, "target")); - - TemplateWizard t = new TemplateWizard(); - t.putProperty("type", "empty"); - t.setTemplate(obj); - t.setTargetFolder(folder); - Set created = t.handleInstantiate(); - assertNotNull(created); - assertEquals("One is created: " + created, 1, created.size()); - - DataObject n = created.iterator().next(); - - assertEquals("Created in right place", folder, n.getFolder()); - assertEquals("Created with right name", fileName[0], n.getName()); - - assertEquals("The right source", fo, Hand.origObject.get(0)); - assertEquals("The right source in query", fo, Hand.acceptObject.get(0)); - assertEquals("The right destiny folder", folder.getPrimaryFile(), Hand.fileObject.get(0)); - assertEquals("The right name", "simpleObject", Hand.name); - assertTrue("At least two elements: " + Hand.parameters, 2 <= Hand.parameters.size()); - assertEquals("empty", Hand.parameters.get("wizard.type")); - assertEquals("There was no name, just default", null, Hand.parameters.get("name")); - assertTrue("the argument is there", Hand.parameters.containsKey("name")); - Object date = Hand.parameters.get("date"); - assertNotNull(date); - assertEquals(String.class, date.getClass()); - Object time = Hand.parameters.get("time"); - assertNotNull(time); - assertEquals(String.class, time.getClass()); - try { - Hand.parameters.put("kuk", "buk"); - } catch (UnsupportedOperationException ex) { - // ok - return obj; - } - fail("Modifications shall be unsupported"); - throw new NullPointerException(); - } - public static final class Hand extends CreateFromTemplateHandler { public static List fileObject, origObject, acceptObject; public static String name; public static Map parameters; - protected boolean accept(FileObject fo) { + public boolean accept(FileObject fo) { acceptObject.add(fo); return true; } - protected FileObject createFromTemplate( + public FileObject createFromTemplate( FileObject orig, FileObject f, String n, Map p ) throws IOException { @@ -223,12 +140,13 @@ } } - public static final class Attr implements CreateFromTemplateAttributesProvider { + public static final class Attr implements CreateFromTemplateAttributes { + @Override public Map attributesFor( - DataObject template, - DataFolder target, + FileObject template, + FileObject target, String name ) { return Collections.singletonMap("name", name); @@ -278,9 +196,6 @@ fail("I do not want to be called"); return null; } - - - } public static final class SimpleObject extends MultiDataObject { diff --git a/openide.loaders/test/unit/src/org/netbeans/modules/templates/IndentEngineIntTest.java b/openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/IndentEngineIntTest.java rename from openide.loaders/test/unit/src/org/netbeans/modules/templates/IndentEngineIntTest.java rename to openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/IndentEngineIntTest.java --- a/openide.loaders/test/unit/src/org/netbeans/modules/templates/IndentEngineIntTest.java +++ b/openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/IndentEngineIntTest.java @@ -37,14 +37,17 @@ import java.awt.Dialog; import java.io.IOException; import java.io.OutputStream; -import java.io.StringWriter; -import java.io.Writer; import java.util.Collections; import java.util.Enumeration; import java.util.Map; -import javax.swing.text.Document; +import javax.swing.text.BadLocationException; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.editor.mimelookup.test.MockMimeLookup; import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.editor.indent.spi.Context; +import org.netbeans.modules.editor.indent.spi.ExtraLock; +import org.netbeans.modules.editor.indent.spi.ReformatTask; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; @@ -58,7 +61,6 @@ import org.openide.loaders.FileEntry; import org.openide.loaders.MultiDataObject; import org.openide.loaders.MultiFileLoader; -import org.openide.text.IndentEngine; import org.openide.util.Enumerations; /** @@ -78,7 +80,8 @@ @SuppressWarnings("deprecation") protected void setUp() throws Exception { - MockServices.setServices(DD.class, Pool.class, IEImpl.class); + MockServices.setServices(DD.class, Pool.class); + MockMimeLookup.setInstances(MimePath.get("text/jarda"), new IEImpl2()); FileUtil.setMIMEType("txt", "text/jarda"); } @@ -183,45 +186,40 @@ return getPrimaryFile().getNameExt(); } } + + public static final class IEImpl2 implements ReformatTask, ReformatTask.Factory { + private Context context; - public static final class IEImpl extends IndentEngine { - - - public int indentLine(Document doc, int offset) { - throw new UnsupportedOperationException("Not supported yet."); + public IEImpl2(Context context) { + this.context = context; } - public int indentNewLine(Document doc, int offset) { - throw new UnsupportedOperationException("Not supported yet."); + public IEImpl2() { + } + + @Override + public void reformat() throws BadLocationException { + int from = context.startOffset(); + int to = context.endOffset(); + int len = to - from; + String s = context.document().getText(from, len); + StringBuilder sb = new StringBuilder(s.length()); + for (int i = s.length() - 1; i >= 0; i--) { + sb.append(s.charAt(i)); + } + context.document().insertString(from, sb.toString(), null); + context.document().remove(from + len, len); } @Override - protected boolean acceptMimeType(String mime) { - return "text/jarda".equals(mime); // NOI18N + public ExtraLock reformatLock() { + return null; } - public Writer createWriter(Document doc, int offset, final Writer writer) { - class Rotate extends StringWriter { - @Override - public void close() throws IOException { - super.close(); - - String s = toString(); - StringBuilder sb = new StringBuilder(s.length()); - for (int i = s.length() - 1; i >= 0; i--) { - sb.append(s.charAt(i)); - } - - writer.write(sb.toString()); - writer.close(); - } - } - - assertNotNull("There is some document", doc); - assertEquals("Its length is 0", 0, doc.getLength()); - assertEquals("Offset is 0", 0, offset); - - return new Rotate(); + @Override + public ReformatTask createTask(Context context) { + return new IEImpl2(context); } + } + } -} diff --git a/openide.loaders/test/unit/src/org/netbeans/modules/templates/SCFTHandlerTest.java b/openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/SCFTHandlerTest.java rename from openide.loaders/test/unit/src/org/netbeans/modules/templates/SCFTHandlerTest.java rename to openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/SCFTHandlerTest.java diff --git a/openide.loaders/test/unit/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateTest.java b/openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateTest.java rename from openide.loaders/test/unit/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateTest.java rename to openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateTest.java --- a/openide.loaders/test/unit/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateTest.java +++ b/openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/ScriptingCreateFromTemplateTest.java @@ -56,6 +56,7 @@ import org.openide.loaders.MultiDataObject; import org.openide.loaders.MultiFileLoader; import org.netbeans.api.editor.mimelookup.test.MockMimeLookup; +import org.netbeans.modules.templates.ScriptingCreateFromTemplateHandler; import org.openide.loaders.CreateFromTemplateHandler; import org.openide.util.SharedClassObject; import org.openide.util.test.MockLookup; diff --git a/openide.loaders/test/unit/src/org/netbeans/modules/templates/utf8.xml b/openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/utf8.xml rename from openide.loaders/test/unit/src/org/netbeans/modules/templates/utf8.xml rename to openide.filesystems.templates/test/unit/src/org/netbeans/modules/templates/utf8.xml diff --git a/openide.filesystems/apichanges.xml b/openide.filesystems/apichanges.xml --- a/openide.filesystems/apichanges.xml +++ b/openide.filesystems/apichanges.xml @@ -49,6 +49,22 @@ Filesystems API + + +

FileLock implements AutoCloseable + + + + + +

+ FileLock + implements AutoCloseable to work well in try-with-resources constructs. +

+
+ + + FileSystem.Status API removed diff --git a/openide.filesystems/manifest.mf b/openide.filesystems/manifest.mf --- a/openide.filesystems/manifest.mf +++ b/openide.filesystems/manifest.mf @@ -2,6 +2,6 @@ OpenIDE-Module: org.openide.filesystems OpenIDE-Module-Localizing-Bundle: org/openide/filesystems/Bundle.properties OpenIDE-Module-Layer: org/openide/filesystems/resources/layer.xml -OpenIDE-Module-Specification-Version: 9.1 +OpenIDE-Module-Specification-Version: 9.2 diff --git a/openide.filesystems/nbproject/project.properties b/openide.filesystems/nbproject/project.properties --- a/openide.filesystems/nbproject/project.properties +++ b/openide.filesystems/nbproject/project.properties @@ -41,7 +41,7 @@ # made subject to such option by the copyright holder. javac.compilerargs=-Xlint -Xlint:-serial -javac.source=1.6 +javac.source=1.7 module.jar.dir=core javadoc.main.page=org/openide/filesystems/doc-files/api.html javadoc.arch=${basedir}/arch.xml diff --git a/openide.filesystems/src/org/openide/filesystems/FileLock.java b/openide.filesystems/src/org/openide/filesystems/FileLock.java --- a/openide.filesystems/src/org/openide/filesystems/FileLock.java +++ b/openide.filesystems/src/org/openide/filesystems/FileLock.java @@ -55,14 +55,17 @@ * Normally this is sufficient protection. If you really need an atomic read, you may * simply lock the file, perform the read, and unlock it when done. The file will still * be protected against writes, although the read operation did not request a lock. -* +*

+* The {@code FileLock} implements {@link AutoCloseable}, so it can be created within +* try-with-resources resource clause and the lock will be released at the end of the try block. +* * @see FileObject -* +* @since 9.2 implements {@code AutoCloseable} interface. * @author Petr Hamernik, Jaroslav Tulach, Ian Formanek * @version 0.16, Jun 5, 1997 * */ -public class FileLock { // XXX JDK 7: implements AutoCloseable +public class FileLock implements AutoCloseable { // ========================= NONE file lock ===================================== /** Constant that can be used in filesystems that do not support locking. @@ -109,6 +112,14 @@ locked = false; } + /** + * Releases the lock. Equivalent to {@link #releaseLock} call. + */ + @Override + public void close() { + releaseLock(); + } + // End of the original part // ============================================================================ diff --git a/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java b/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java --- a/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java +++ b/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java @@ -283,23 +283,20 @@ FileObject fold = getTestFolder1(root); FileObject fo1 = getTestFile1(fold); - FileLock lock; - try { - lock = fo1.lock(); + // use the new AutoCloseable feature + try (FileLock lock = fo1.lock()) { + fo1.rename(lock, "Aaa", "java"); + assertEquals("Name is Aaa", "Aaa", fo1.getName()); + fo1.rename(lock, "bbb", "java"); + assertEquals("Name is bbb", "bbb", fo1.getName()); + fo1.rename(lock, "aaa", "java"); + assertEquals("Name is lowercase", "aaa", fo1.getName()); } catch (IOException iex) { fsAssert( "expected copy will success on writable FS", fs.isReadOnly() || fo1.isReadOnly() ); - return; } - fo1.rename(lock, "Aaa", "java"); - assertEquals("Name is Aaa", "Aaa", fo1.getName()); - fo1.rename(lock, "bbb", "java"); - assertEquals("Name is bbb", "bbb", fo1.getName()); - fo1.rename(lock, "aaa", "java"); - assertEquals("Name is lowercase", "aaa", fo1.getName()); - lock.releaseLock(); } /** Test of copy method, of class org.openide.filesystems.FileObject. */ @@ -540,16 +537,12 @@ FileObject fold = getTestFolder1(root); FileObject fo1 = getTestFile1(fold); FileObject fo2 = getTestFile2(fold); - FileLock lock = null; - try { - lock = fo1.lock(); + try (FileLock lock = fo1.lock()) { fo1.move(lock, fold,fo2.getName(),fo2.getExt()); } catch (IOException iex) { /** Test passed*/ return; - } finally { - if (lock != null) lock.releaseLock(); } fsFail ("move should fire exception if file already exists"); } diff --git a/openide.loaders/apichanges.xml b/openide.loaders/apichanges.xml --- a/openide.loaders/apichanges.xml +++ b/openide.loaders/apichanges.xml @@ -109,6 +109,25 @@ + + +

Separate template handling + + + + + + Template handling need not depend on Data System APIs, should be available + for clients that only know FileSystems. Relevant interfaces moved to + openide.filesystems.templates module; see javadoc for + + TemplateUtils for details. + + + + +
Introduce targetName for templates. diff --git a/openide.loaders/manifest.mf b/openide.loaders/manifest.mf --- a/openide.loaders/manifest.mf +++ b/openide.loaders/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.openide.loaders -OpenIDE-Module-Specification-Version: 7.58 +OpenIDE-Module-Specification-Version: 7.59 OpenIDE-Module-Localizing-Bundle: org/openide/loaders/Bundle.properties OpenIDE-Module-Provides: org.netbeans.modules.templates.v1_0 OpenIDE-Module-Layer: org/netbeans/modules/openide/loaders/layer.xml diff --git a/openide.loaders/nbproject/project.xml b/openide.loaders/nbproject/project.xml --- a/openide.loaders/nbproject/project.xml +++ b/openide.loaders/nbproject/project.xml @@ -142,6 +142,14 @@
+ org.openide.filesystems.templates + + + + 1.0 + + + org.openide.modules diff --git a/openide.loaders/src/org/netbeans/modules/templates/FileTemplateHandlerBridge.java b/openide.loaders/src/org/netbeans/modules/templates/FileTemplateHandlerBridge.java new file mode 100644 --- /dev/null +++ b/openide.loaders/src/org/netbeans/modules/templates/FileTemplateHandlerBridge.java @@ -0,0 +1,100 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2014 Sun Microsystems, Inc. + */ +package org.netbeans.modules.templates; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.openide.filesystems.FileObject; +import org.netbeans.api.templates.CreateFromTemplateAttributes; +import org.openide.loaders.CreateFromTemplateAttributesProvider; +import org.openide.loaders.DataFolder; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObjectNotFoundException; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; + +/** + * Bridges loader-based handler registration to the fileobject-based one. Provides + * compatibility with NB <= 8.0.1. New clients are encouraged to use the new + * {@link CreateFromTemplateAttributes} interface directly. + * + * @author sdedic + */ +public class FileTemplateHandlerBridge implements CreateFromTemplateAttributes { + private Lookup.Result providers; + + public FileTemplateHandlerBridge() { + providers = Lookup.getDefault().lookupResult(CreateFromTemplateAttributesProvider.class); + } + + @Override + public Map attributesFor(FileObject template, FileObject target, String name) { + Collection c = providers.allInstances(); + if (c.isEmpty()) { + return Collections.emptyMap(); + } + DataObject d; + DataFolder fld; + + try { + d = DataObject.find(template); + fld = DataFolder.findFolder(target); + } catch (DataObjectNotFoundException ex) { + // ??? + Exceptions.printStackTrace(ex); + return Collections.emptyMap(); + } + HashMap all = new HashMap(); + for (CreateFromTemplateAttributesProvider p : c) { + Map map = p.attributesFor(d, fld, name); + if (map != null) { + for (Map.Entry e : map.entrySet()) { + all.put(e.getKey(), e.getValue()); + } + } + } + + return all; + } +} diff --git a/openide.loaders/src/org/netbeans/modules/templates/TemplateProcessor.java b/openide.loaders/src/org/netbeans/modules/templates/TemplateProcessor.java --- a/openide.loaders/src/org/netbeans/modules/templates/TemplateProcessor.java +++ b/openide.loaders/src/org/netbeans/modules/templates/TemplateProcessor.java @@ -38,8 +38,6 @@ package org.netbeans.modules.templates; -import java.net.URI; -import java.net.URISyntaxException; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -58,6 +56,14 @@ import org.openide.filesystems.annotations.LayerGeneratingProcessor; import org.openide.util.lookup.ServiceProvider; +/** + * The processor was split into two parts. The part which defines how the template + * annotation fields will be translated into the XML layer is defined in {@code openide.filesystems.templates} + * module. This Processor implementation binds the template to a Wizard Iterator, if the annotation is on + * an executable method. + * + * @author sdedic + */ @ServiceProvider(service=Processor.class) @SupportedSourceVersion(SourceVersion.RELEASE_7) public class TemplateProcessor extends LayerGeneratingProcessor { @@ -86,7 +92,7 @@ process(e, t); } } - return true; + return false; } private void process(Element e, TemplateRegistration t) throws LayerGenerationException { @@ -110,61 +116,13 @@ } String folder = "Templates/" + t.folder() + '/'; LayerBuilder.File f = builder.file(folder + basename); - f.boolvalue("template", true); - f.position(t.position()); - if (!t.displayName().isEmpty()) { - f.bundlevalue("displayName", t.displayName()); - } - if (!t.iconBase().isEmpty()) { - builder.validateResource(t.iconBase(), e, t, "iconBase", true); - f.stringvalue("iconBase", t.iconBase()); - } else if (t.content().length == 0) { - throw new LayerGenerationException("Must specify iconBase if content is not specified", e, processingEnv, t); - } - if (!t.description().isEmpty()) { - f.urlvalue("instantiatingWizardURL", contentURI(e, t.description(), builder, t, "description")); - } if (e.getKind() != ElementKind.PACKAGE) { f.instanceAttribute("instantiatingIterator", InstantiatingIterator.class); } - if (t.content().length > 0) { - f.url(contentURI(e, t.content()[0], builder, t, "content").toString()); - for (int i = 1; i < t.content().length; i++) { - builder.file(folder + basename(t.content()[i])).url(contentURI(e, t.content()[i], builder, t, "content").toString()).position(0).write(); - } - } - if (!t.scriptEngine().isEmpty()) { - f.stringvalue(ScriptingCreateFromTemplateHandler.SCRIPT_ENGINE_ATTR, t.scriptEngine()); - } - if (t.category().length > 0) { - StringBuilder sb = new StringBuilder(); - for (String c : t.category()) { - if (sb.length() > 0) { - sb.append(','); - } - sb.append(c); - } - f.stringvalue("templateCategory", sb.toString()); - } - f.boolvalue("requireProject", t.requireProject()); - if (!t.targetName().trim().isEmpty()) { - f.bundlevalue("targetName", t.targetName()); //NOI18N - } f.write(); } private static String basename(String relativeResource) { return relativeResource.replaceFirst(".+/", "").replaceFirst("[.]template$", ""); } - - private URI contentURI(Element e, String relativePath, LayerBuilder builder, TemplateRegistration t, String annotationMethod) throws LayerGenerationException { - String path = LayerBuilder.absolutizeResource(e, relativePath); - builder.validateResource(path, e, t, annotationMethod, false); - try { - return new URI("nbresloc", "/" + path, null).normalize(); - } catch (URISyntaxException x) { - throw new LayerGenerationException("could not translate " + path, e, processingEnv, t); - } - } - } diff --git a/openide.loaders/src/org/openide/actions/SaveAsTemplateAction.java b/openide.loaders/src/org/openide/actions/SaveAsTemplateAction.java --- a/openide.loaders/src/org/openide/actions/SaveAsTemplateAction.java +++ b/openide.loaders/src/org/openide/actions/SaveAsTemplateAction.java @@ -50,7 +50,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import org.netbeans.modules.templates.ScriptingCreateFromTemplateHandler; import org.openide.cookies.SaveCookie; import org.openide.filesystems.FileObject; import org.openide.loaders.*; @@ -136,6 +135,8 @@ protected boolean asynchronous() { return false; } + + static final String SCRIPT_ENGINE_ATTR = "javax.script.ScriptEngine"; // NOI18N /** Performs the work of creating a new template */ private void createNewTemplate(DataObject source, @@ -156,7 +157,7 @@ newTemplate.setTemplate(true); if (templateSample == null) { // a fallback if no template sample found - newTemplate.getPrimaryFile().setAttribute(ScriptingCreateFromTemplateHandler.SCRIPT_ENGINE_ATTR, "freemarker"); // NOI18N + newTemplate.getPrimaryFile().setAttribute(SCRIPT_ENGINE_ATTR, "freemarker"); // NOI18N } else { setTemplateAttributes (newTemplate.getPrimaryFile (), getAttributes (templateSample.getPrimaryFile ())); } diff --git a/openide.loaders/src/org/openide/loaders/CreateFromTemplateAttributesProvider.java b/openide.loaders/src/org/openide/loaders/CreateFromTemplateAttributesProvider.java --- a/openide.loaders/src/org/openide/loaders/CreateFromTemplateAttributesProvider.java +++ b/openide.loaders/src/org/openide/loaders/CreateFromTemplateAttributesProvider.java @@ -41,9 +41,18 @@ * and allows anyone provide additional parameters to each {@link CreateFromTemplateHandler}s * when a template is instantiating. * Read more in the howto document. + *

+ * Since templating system need not to depend on Data Systems APIs, the relevant interfaces + * were moved to the {@code openide.filesystems.templates} module. This interface has been kept + * for backward compatibility and DataSystems provide a compatibility bridge, which allows + * old providers to participate. Module writers are encouraged to implement + * {@link org.netbeans.api.templates.CreateFromTemplateAttributes} + * instead. * * @author Jaroslav Tulach * @since 6.3 + * @since deprecated from 7.59 + * @deprecated Use {@link CreateFromTemplateAttributes} in {@code openide.filesystems.templates} instead. */ public interface CreateFromTemplateAttributesProvider { /** Called when a template is about to be instantiated to provide additional diff --git a/openide.loaders/src/org/openide/loaders/CreateFromTemplateHandler.java b/openide.loaders/src/org/openide/loaders/CreateFromTemplateHandler.java --- a/openide.loaders/src/org/openide/loaders/CreateFromTemplateHandler.java +++ b/openide.loaders/src/org/openide/loaders/CreateFromTemplateHandler.java @@ -46,44 +46,7 @@ * * @author Jaroslav Tulach * @since 6.1 + * @deprecated in 7.59. Use {@link org.netbeans.api.templates.CreateFromTemplateHandler} instead. */ -public abstract class CreateFromTemplateHandler { - /** Method that allows a handler to reject a file. If all handlers - * reject a file, regular processing defined in {@link DataObject#handleCreateFromTemplate} - * is going to take place. - * - * @param orig the file of the template - * @return true if this handler wants to handle the createFromTemplate operation - */ - protected abstract boolean accept(FileObject orig); - - /** Handles the creation of new file. - * @param orig the source file - * @param f the folder to create a file in - * @param name the name of new file to create in the folder (see {@link #FREE_FILE_EXTENSION} regarding extension) - * @param parameters map of additional arguments as specified by registered {@link CreateFromTemplateAttributesProvider}s - * @return the newly create file - * @throws IOException if something goes wrong with I/O - */ - protected abstract FileObject createFromTemplate( - FileObject orig, - FileObject f, - String name, - Map parameters - ) throws IOException; - - /** - * Parameter to enable free file extension mode. - * By default, the extension of the newly created file will be inherited - * from the template. But if {@link #createFromTemplate} is called with this - * parameter set to {@link Boolean#TRUE} - * (such as from {@link DataObject#createFromTemplate(DataFolder,String,Map)}), - * and the file name already seems to - * include an extension (*.*), the handler should not append - * any extension from the template. - * @since org.openide.loaders 7.16 - * @see Templates.SimpleTargetChooserBuilder.freeFileExtension - */ - public static final String FREE_FILE_EXTENSION = "freeFileExtension"; // NOI18N - +public abstract class CreateFromTemplateHandler extends org.netbeans.api.templates.CreateFromTemplateHandler { } diff --git a/openide.loaders/src/org/openide/loaders/DataObject.java b/openide.loaders/src/org/openide/loaders/DataObject.java --- a/openide.loaders/src/org/openide/loaders/DataObject.java +++ b/openide.loaders/src/org/openide/loaders/DataObject.java @@ -1601,6 +1601,14 @@ } } + public static Map getCallParameters(String name) { + CreateAction c = CURRENT.get(); + if (c == null || c.param == null) { + return Collections.emptyMap(); + } + return Collections.unmodifiableMap(c.param); + } + public static Map findParameters(String name) { CreateAction c = CURRENT.get(); if (c == null) { diff --git a/openide.loaders/src/org/openide/loaders/FileEntry.java b/openide.loaders/src/org/openide/loaders/FileEntry.java --- a/openide.loaders/src/org/openide/loaders/FileEntry.java +++ b/openide.loaders/src/org/openide/loaders/FileEntry.java @@ -45,6 +45,8 @@ package org.openide.loaders; import java.io.*; +import java.util.Map; +import org.netbeans.api.templates.TemplateUtils; import org.openide.filesystems.*; import org.openide.util.Lookup; import org.openide.util.NbBundle; @@ -149,32 +151,11 @@ * @param f the folder to create instance in * @param name name of the file or null if it should be choosen automaticly */ + @Override public FileObject createFromTemplate (FileObject f, String name) throws IOException { - if (name == null) { - name = FileUtil.findFreeFileName( - f, - getFile ().getName (), getFile ().getExt () - ); - } - - - FileObject fo = null; - for (CreateFromTemplateHandler h : Lookup.getDefault().lookupAll(CreateFromTemplateHandler.class)) { - if (h.accept(getFile())) { - fo = h.createFromTemplate(getFile(), f, name, - DataObject.CreateAction.enhanceParameters( - DataObject.CreateAction.findParameters(name), - name, getFile().getExt())); - assert fo != null; - break; - } - } - - if (fo == null) { - fo = getFile().copy (f, name, getFile().getExt ()); - } - - + FileObject fo = TemplateUtils.createFromTemplate(getFile(), f, name, + DataObject.CreateAction.getCallParameters(name), + TemplateUtils.Mode.COPY); // unmark template state DataObject.setTemplate (fo, false); @@ -204,65 +185,19 @@ * @param f the folder to create instance in * @param name name of the file or null if it should be choosen automaticly */ + @Override + @SuppressWarnings("AssignmentToMethodParameter") public FileObject createFromTemplate (FileObject f, String name) throws IOException { String ext = getFile ().getExt (); - if (name == null) { name = FileUtil.findFreeFileName( f, getFile ().getName (), ext ); } - - FileObject fo = null; - for (CreateFromTemplateHandler h : Lookup.getDefault().lookupAll(CreateFromTemplateHandler.class)) { - if (h.accept(getFile())) { - fo = h.createFromTemplate( - getFile(), f, name, - DataObject.CreateAction.enhanceParameters( - DataObject.CreateAction.findParameters(name), - name, getFile().getExt())); - assert fo != null; - break; - } - } - - if (fo != null) { - // unmark template state - DataObject.setTemplate (fo, false); - return fo; - } - - fo = f.createData (name, ext); - java.text.Format frm = createFormat (f, name, ext); - - BufferedReader r = new BufferedReader (new InputStreamReader (getFile ().getInputStream ())); - try { - FileLock lock = fo.lock (); - try { - BufferedWriter w = new BufferedWriter (new OutputStreamWriter (fo.getOutputStream (lock))); - - try { - String current; - while ((current = r.readLine ()) != null) { - w.write (frm.format (current)); - // Cf. #7061. - w.newLine (); - } - } finally { - w.close (); - } - } finally { - lock.releaseLock (); - } - } finally { - r.close (); - } - - // copy attributes - FileUtil.copyAttributes (getFile (), fo); - + FileObject fo = TemplateUtils.createFromTemplate(getFile(), f, name, + DataObject.CreateAction.getCallParameters(name), frm); // unmark template state DataObject.setTemplate (fo, false); diff --git a/openide.loaders/src/org/openide/loaders/MultiDataObject.java b/openide.loaders/src/org/openide/loaders/MultiDataObject.java --- a/openide.loaders/src/org/openide/loaders/MultiDataObject.java +++ b/openide.loaders/src/org/openide/loaders/MultiDataObject.java @@ -49,24 +49,18 @@ import java.beans.PropertyVetoException; import java.io.*; import java.lang.ref.WeakReference; -import java.lang.reflect.Method; import java.util.*; -import java.util.concurrent.Callable; import java.util.logging.*; import javax.swing.event.*; import org.netbeans.api.actions.Editable; import org.netbeans.api.actions.Openable; +import org.netbeans.api.templates.TemplateUtils; import org.openide.cookies.CloseCookie; import org.openide.cookies.EditorCookie; import org.openide.cookies.LineCookie; -import org.openide.cookies.PrintCookie; import org.openide.filesystems.*; import org.openide.nodes.*; -import org.openide.nodes.Node.Cookie; import org.openide.text.CloneableEditor; -import org.openide.text.CloneableEditorSupport; -import org.openide.text.CloneableEditorSupport.Pane; -import org.openide.text.DataEditorSupport; import org.openide.util.*; /** Provides support for handling of data objects with multiple files. @@ -876,21 +870,9 @@ } FileObject pf = null; - Map params = null; - for (CreateFromTemplateHandler h : Lookup.getDefault().lookupAll(CreateFromTemplateHandler.class)) { - FileObject current = getPrimaryEntry().getFile(); - if (h.accept(current)) { - if (params == null) { - params = DataObject.CreateAction.findParameters(name); - } - pf = h.createFromTemplate(current, df.getPrimaryFile(), name, - DataObject.CreateAction.enhanceParameters(params, name, current.getExt()) - ); - assert pf != null; - break; - } - } - if (params == null) { + Map params = CreateAction.getCallParameters(name); + pf = TemplateUtils.createFromTemplate(getPrimaryFile(), df.getPrimaryFile(), name, params, TemplateUtils.Mode.FAIL); + if (pf == null) { // do the regular creation pf = getPrimaryEntry().createFromTemplate (df.getPrimaryFile (), name); } @@ -899,20 +881,11 @@ Iterator it = secondaryEntries().iterator(); NEXT_ENTRY: while (it.hasNext ()) { Entry entry = it.next(); - for (CreateFromTemplateHandler h : Lookup.getDefault().lookupAll(CreateFromTemplateHandler.class)) { - FileObject current = entry.getFile(); - if (h.accept(current)) { - if (params == null) { - params = DataObject.CreateAction.findParameters(name); - } - FileObject fo = h.createFromTemplate(current, df.getPrimaryFile(), name, - DataObject.CreateAction.enhanceParameters(params, name, current.getExt()) - ); - assert fo != null; - continue NEXT_ENTRY; - } + FileObject current = entry.getFile(); + FileObject fo = TemplateUtils.createFromTemplate(current, df.getPrimaryFile(), name, params, TemplateUtils.Mode.FAIL); + if (fo == null) { + entry.createFromTemplate (df.getPrimaryFile (), name); } - entry.createFromTemplate (df.getPrimaryFile (), name); } try { diff --git a/openide.loaders/test/unit/src/org/openide/loaders/CreateFromTemplateHandlerTest.java b/openide.loaders/test/unit/src/org/openide/loaders/CreateFromTemplateHandlerTest.java --- a/openide.loaders/test/unit/src/org/openide/loaders/CreateFromTemplateHandlerTest.java +++ b/openide.loaders/test/unit/src/org/openide/loaders/CreateFromTemplateHandlerTest.java @@ -205,12 +205,12 @@ public static String name; public static Map parameters; - protected boolean accept(FileObject fo) { + public boolean accept(FileObject fo) { acceptObject.add(fo); return true; } - protected FileObject createFromTemplate( + public FileObject createFromTemplate( FileObject orig, FileObject f, String n, Map p ) throws IOException { diff --git a/openide.util.base/src/org/openide/util/BaseUtilities.java b/openide.util.base/src/org/openide/util/BaseUtilities.java --- a/openide.util.base/src/org/openide/util/BaseUtilities.java +++ b/openide.util.base/src/org/openide/util/BaseUtilities.java @@ -1695,5 +1695,5 @@ */ public String[] readPair(String line); } - + } diff --git a/openide.util.base/src/org/openide/util/UserInformation.java b/openide.util.base/src/org/openide/util/UserInformation.java new file mode 100644 --- /dev/null +++ b/openide.util.base/src/org/openide/util/UserInformation.java @@ -0,0 +1,170 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2014 Sun Microsystems, Inc. + */ +package org.openide.util; + +import java.util.Locale; +import java.util.TimeZone; +import org.openide.util.spi.UserInfoProvider; + +/** + * Provides the application user information. While desktop user information + * can be read from JDK properties and defaults, in multi-user environment + * the current user information must be obtained in a different way. This class + * encapsulates the data, so the client code may remain independent of the + * deployment details. + * + * @author sdedic + */ +public final class UserInformation { + /** + * The user ID, as identified to the system + */ + private String login; + + /** + * User's display name + */ + private String userName; + + /** + * The user's locale + */ + private Locale locale; + + /** + * The user's time zone + */ + private TimeZone timezone; + + private boolean locked; + + public UserInformation() { + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + checkLocked(); + this.login = login; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + checkLocked(); + this.userName = userName; + } + + public Locale getLocale() { + return locale; + } + + public void setLocale(Locale locale) { + checkLocked(); + this.locale = locale; + } + + public TimeZone getTimezone() { + return timezone; + } + + public void setTimezone(TimeZone timezone) { + checkLocked(); + this.timezone = timezone; + } + + UserInformation lock() { + locked = true; + return this; + } + + private void checkLocked() { + if (locked) { + throw new UnsupportedOperationException(); + } + } + + private static final UserInfoProvider JDK_PROVIDER = new JDKUserProvider(); + + /** + * Provides user information relevant for the calling thread. + * @return + */ + public static UserInformation getInfo() { + return getInfo(Lookup.getDefault()); + } + + /** + * Provides user information applicable to the given context. If the Lookup + * does not contain {@link UserInfoProvider} implementation, the default + * JDK provider will be used. + * + * @return user information + * @param ctx the execution context + */ + public static UserInformation getInfo(Lookup ctx) { + assert ctx != null; + + UserInfoProvider p = Lookup.getDefault().lookup(UserInfoProvider.class); + return (p == null ? JDK_PROVIDER : p).findUserInfo().lock(); + } + + private static final class JDKUserProvider implements UserInfoProvider { + private final UserInformation info = new UserInformation(); + + public JDKUserProvider() { + info.setLogin(System.getProperty("user.name")); + info.setUserName(System.getProperty("user.name")); + info.setTimezone(TimeZone.getDefault()); + info.setLocale(Locale.getDefault()); + } + + @Override + public UserInformation findUserInfo() { + return info; + } + } +} diff --git a/openide.util.base/src/org/openide/util/spi/UserInfoProvider.java b/openide.util.base/src/org/openide/util/spi/UserInfoProvider.java new file mode 100644 --- /dev/null +++ b/openide.util.base/src/org/openide/util/spi/UserInfoProvider.java @@ -0,0 +1,52 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2014 Sun Microsystems, Inc. + */ +package org.openide.util.spi; + +import org.openide.util.UserInformation; + +/** + * + * @author sdedic + */ +public interface UserInfoProvider { + public UserInformation findUserInfo(); +}