diff options
15 files changed, 1393 insertions, 785 deletions
diff --git a/org.eclipse.jface.text/.settings/.api_filters b/org.eclipse.jface.text/.settings/.api_filters index 7d35f4ab134..1396277426a 100644 --- a/org.eclipse.jface.text/.settings/.api_filters +++ b/org.eclipse.jface.text/.settings/.api_filters @@ -8,4 +8,20 @@ </message_arguments> </filter> </resource> + <resource path="src/org/eclipse/jface/text/templates/persistence/TemplatePersistenceData.java" type="org.eclipse.jface.text.templates.persistence.TemplatePersistenceData"> + <filter comment="Extended to prevent API breakage" id="571473929"> + <message_arguments> + <message_argument value="TemplatePersistenceData"/> + <message_argument value="TemplatePersistenceData"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jface/text/templates/persistence/TemplateReaderWriter.java" type="org.eclipse.jface.text.templates.persistence.TemplateReaderWriter"> + <filter comment="Extended to prevent API breakage" id="571473929"> + <message_arguments> + <message_argument value="TemplateReaderWriter"/> + <message_argument value="TemplateReaderWriter"/> + </message_arguments> + </filter> + </resource> </component> diff --git a/org.eclipse.jface.text/META-INF/MANIFEST.MF b/org.eclipse.jface.text/META-INF/MANIFEST.MF index f4f372686ed..b2503aac2bb 100644 --- a/org.eclipse.jface.text/META-INF/MANIFEST.MF +++ b/org.eclipse.jface.text/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jface.text -Bundle-Version: 3.13.200.qualifier +Bundle-Version: 3.14.0.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: diff --git a/org.eclipse.jface.text/pom.xml b/org.eclipse.jface.text/pom.xml index c80e04c3b97..14c4ad68d2d 100644 --- a/org.eclipse.jface.text/pom.xml +++ b/org.eclipse.jface.text/pom.xml @@ -18,6 +18,6 @@ </parent> <groupId>org.eclipse.jface</groupId> <artifactId>org.eclipse.jface.text</artifactId> - <version>3.13.200-SNAPSHOT</version> + <version>3.14.0-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/ContextTypeRegistry.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/ContextTypeRegistry.java index cb8b9f68ea0..74cf8ef7d60 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/ContextTypeRegistry.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/ContextTypeRegistry.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2018 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,8 +11,6 @@ package org.eclipse.jface.text.templates; import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; /** * A registry for context types. Editor implementors will usually instantiate a @@ -23,38 +21,23 @@ import java.util.Map; * </p> * * @since 3.0 + * @deprecated See {@link ContextTypeRegistry} from org.eclipse.text */ -public class ContextTypeRegistry { +@Deprecated +public class ContextTypeRegistry extends org.eclipse.text.templates.ContextTypeRegistry { - /** all known context types */ - private final Map<String, TemplateContextType> fContextTypes= new LinkedHashMap<>(); - - /** - * Adds a context type to the registry. If there already is a context type - * with the same ID registered, it is replaced. - * - * @param contextType the context type to add - */ + @Override public void addContextType(TemplateContextType contextType) { - fContextTypes.put(contextType.getId(), contextType); + super.addContextType(contextType); } - /** - * Returns the context type if the id is valid, <code>null</code> otherwise. - * - * @param id the id of the context type to retrieve - * @return the context type if <code>name</code> is valid, <code>null</code> otherwise - */ + @Override public TemplateContextType getContextType(String id) { - return fContextTypes.get(id); + return super.getContextType(id); } - /** - * Returns an iterator over all registered context types. - * - * @return an iterator over all registered context types - */ + @Override public Iterator<TemplateContextType> contextTypes() { - return fContextTypes.values().iterator(); + return super.contextTypes(); } } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplatePersistenceData.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplatePersistenceData.java index 607125a5271..8d1dbbfb57b 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplatePersistenceData.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplatePersistenceData.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. + * Copyright (c) 2000, 2018 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,8 +10,6 @@ *******************************************************************************/ package org.eclipse.jface.text.templates.persistence; -import org.eclipse.core.runtime.Assert; - import org.eclipse.jface.text.templates.Template; @@ -27,154 +25,113 @@ import org.eclipse.jface.text.templates.Template; * * @since 3.0 * @noextend This class is not intended to be subclassed by clients. + * @deprecated See {@link org.eclipse.text.templates.TemplatePersistenceData} */ -public class TemplatePersistenceData { - private final Template fOriginalTemplate; - private final String fId; - private final boolean fOriginalIsEnabled; +@Deprecated +public class TemplatePersistenceData extends org.eclipse.text.templates.TemplatePersistenceData { - private Template fCustomTemplate= null; - private boolean fIsDeleted= false; - private boolean fCustomIsEnabled= true; + org.eclipse.text.templates.TemplatePersistenceData ref; /** - * Creates a new, user-added instance that is not linked to a contributed - * template. + * In some cases, we must continue to respect the deprecated TemplatePresistenceData + * even though we are given {@link org.eclipse.text.templates.TemplatePersistenceData}. * - * @param template the template which is stored by the new instance - * @param enabled whether the template is enabled + * @param data The {@link org.eclipse.text.templates.TemplatePersistenceData} that will + * underlie this object. + * @since 3.14 */ + public TemplatePersistenceData(org.eclipse.text.templates.TemplatePersistenceData data) { + super(data.getTemplate(), data.isEnabled(), data.getId()); // these are ignored + this.ref= data; + } + public TemplatePersistenceData(Template template, boolean enabled) { - this(template, enabled, null); + super(template, enabled); } - /** - * Creates a new instance. If <code>id</code> is not <code>null</code>, - * the instance is represents a template that is contributed and can be - * identified via its id. - * - * @param template the template which is stored by the new instance - * @param enabled whether the template is enabled - * @param id the id of the template, or <code>null</code> if a user-added - * instance should be created - */ public TemplatePersistenceData(Template template, boolean enabled, String id) { - Assert.isNotNull(template); - fOriginalTemplate= template; - fCustomTemplate= template; - fOriginalIsEnabled= enabled; - fCustomIsEnabled= enabled; - fId= id; + super(template, enabled, id); } - /** - * Returns the id of this template store, or <code>null</code> if there is none. - * - * @return the id of this template store - */ + @Override public String getId() { - return fId; + return (ref != null) ? ref.getId() : super.getId(); } - /** - * Returns the deletion state of the stored template. This is only relevant - * of contributed templates. - * - * @return the deletion state of the stored template - */ + @Override public boolean isDeleted() { - return fIsDeleted; + return (ref != null) ? ref.isDeleted() : super.isDeleted(); } - /** - * Sets the deletion state of the stored template. - * - * @param isDeleted the deletion state of the stored template - */ + @Override public void setDeleted(boolean isDeleted) { - fIsDeleted= isDeleted; + if (ref != null) { + ref.setDeleted(isDeleted); + } else { + super.setDeleted(isDeleted); + } } - /** - * Returns the template encapsulated by the receiver. - * - * @return the template encapsulated by the receiver - */ + @Override public Template getTemplate() { - return fCustomTemplate; + return (ref != null) ? ref.getTemplate() : super.getTemplate(); } - - /** - * Sets the template encapsulated by the receiver. - * - * @param template the new template - */ + @Override public void setTemplate(Template template) { - fCustomTemplate= template; + if (ref != null) { + ref.setTemplate(template); + } else { + super.setTemplate(template); + } } - /** - * Returns whether the receiver represents a custom template, i.e. is either - * a user-added template or a contributed template that has been modified. - * - * @return <code>true</code> if the contained template is a custom - * template and cannot be reconstructed from the contributed - * templates - */ + @Override public boolean isCustom() { - return fId == null - || fIsDeleted - || fOriginalIsEnabled != fCustomIsEnabled - || !fOriginalTemplate.equals(fCustomTemplate); + return (ref != null) ? ref.isCustom() : super.isCustom(); } - /** - * Returns whether the receiver represents a modified template, i.e. a - * contributed template that has been changed. - * - * @return <code>true</code> if the contained template is contributed but has been modified, <code>false</code> otherwise - */ + @Override public boolean isModified() { - return isCustom() && !isUserAdded(); + return (ref != null) ? ref.isModified() : super.isModified(); } - /** - * Returns <code>true</code> if the contained template was added by a - * user, i.e. does not reference a contributed template. - * - * @return <code>true</code> if the contained template was added by a user, <code>false</code> otherwise - */ + @Override public boolean isUserAdded() { - return fId == null; + return (ref != null) ? ref.isUserAdded() : super.isUserAdded(); } - - /** - * Reverts the template to its original setting. - */ + @Override public void revert() { - fCustomTemplate= fOriginalTemplate; - fCustomIsEnabled= fOriginalIsEnabled; - fIsDeleted= false; + if (ref != null) { + ref.revert(); + } else { + super.revert(); + } } - - /** - * Returns the enablement state of the contained template. - * - * @return the enablement state of the contained template - */ + @Override public boolean isEnabled() { - return fCustomIsEnabled; + return (ref != null) ? ref.isEnabled() : super.isEnabled(); } - /** - * Sets the enablement state of the contained template. - * - * @param isEnabled the new enablement state of the contained template - */ + @Override public void setEnabled(boolean isEnabled) { - fCustomIsEnabled= isEnabled; + if (ref != null) { + ref.setEnabled(isEnabled); + } else { + super.setEnabled(isEnabled); + } } + + @Override + public boolean equals(Object other) { + return (ref != null) ? ref.equals(other) : super.equals(other); + } + + @Override + public int hashCode() { + return (ref != null) ? ref.hashCode() : super.hashCode(); + } + } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateReaderWriter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateReaderWriter.java index e54dd929490..109ad5a0382 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateReaderWriter.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateReaderWriter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2016 IBM Corporation and others. + * Copyright (c) 2000, 2018 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -15,43 +15,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.MissingResourceException; import java.util.ResourceBundle; -import java.util.Set; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.w3c.dom.Text; - -import org.eclipse.osgi.util.NLS; - -import org.eclipse.core.runtime.Assert; -import org.eclipse.core.runtime.ILog; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Platform; -import org.eclipse.core.runtime.Status; - -import org.eclipse.jface.text.templates.Template; /** @@ -63,353 +27,56 @@ import org.eclipse.jface.text.templates.Template; * * @since 3.0 * @noextend This class is not intended to be subclassed by clients. + * @deprecated See {@link org.eclipse.text.templates.TemplateReaderWriter} */ -public class TemplateReaderWriter { - - private static final String TEMPLATE_ROOT = "templates"; //$NON-NLS-1$ - private static final String TEMPLATE_ELEMENT = "template"; //$NON-NLS-1$ - private static final String NAME_ATTRIBUTE= "name"; //$NON-NLS-1$ - private static final String ID_ATTRIBUTE= "id"; //$NON-NLS-1$ - private static final String DESCRIPTION_ATTRIBUTE= "description"; //$NON-NLS-1$ - private static final String CONTEXT_ATTRIBUTE= "context"; //$NON-NLS-1$ - private static final String ENABLED_ATTRIBUTE= "enabled"; //$NON-NLS-1$ - private static final String DELETED_ATTRIBUTE= "deleted"; //$NON-NLS-1$ - /** - * @since 3.1 - */ - private static final String AUTO_INSERTABLE_ATTRIBUTE= "autoinsert"; //$NON-NLS-1$ +@Deprecated +public class TemplateReaderWriter extends org.eclipse.text.templates.TemplateReaderWriter { - /** - * Create a new instance. - */ public TemplateReaderWriter() { } - /** - * Reads templates from a reader and returns them. The reader must present - * a serialized form as produced by the <code>save</code> method. - * - * @param reader the reader to read templates from - * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code> - * @throws IOException if reading from the stream fails - */ + @Override public TemplatePersistenceData[] read(Reader reader) throws IOException { - return read(reader, null); + org.eclipse.text.templates.TemplatePersistenceData[] list= super.read(reader); + TemplatePersistenceData[] result= new TemplatePersistenceData[list.length]; + for (int i= 0; i < list.length; i++) { + result[i]= new TemplatePersistenceData(list[i]); + } + return result; } - /** - * Reads the template with identifier <code>id</code> from a reader and - * returns it. The reader must present a serialized form as produced by the - * <code>save</code> method. - * - * @param reader the reader to read templates from - * @param id the id of the template to return - * @return the read template, encapsulated in an instances of - * <code>TemplatePersistenceData</code> - * @throws IOException if reading from the stream fails - * @since 3.1 - */ + @Override public TemplatePersistenceData readSingle(Reader reader, String id) throws IOException { - TemplatePersistenceData[] datas= read(new InputSource(reader), null, id); - if (datas.length > 0) - return datas[0]; - return null; + return new org.eclipse.jface.text.templates.persistence.TemplatePersistenceData(super.readSingle(reader, id)); } - /** - * Reads templates from a stream and adds them to the templates. - * - * @param reader the reader to read templates from - * @param bundle a resource bundle to use for translating the read templates, or <code>null</code> if no translation should occur - * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code> - * @throws IOException if reading from the stream fails - */ + @Override public TemplatePersistenceData[] read(Reader reader, ResourceBundle bundle) throws IOException { - return read(new InputSource(reader), bundle, null); + org.eclipse.text.templates.TemplatePersistenceData[] list= super.read(reader, bundle); + TemplatePersistenceData[] result= new TemplatePersistenceData[list.length]; + for (int i= 0; i < list.length; i++) { + result[i]= new TemplatePersistenceData(list[i]); + } + return result; } - /** - * Reads templates from a stream and adds them to the templates. - * - * @param stream the byte stream to read templates from - * @param bundle a resource bundle to use for translating the read templates, or <code>null</code> if no translation should occur - * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code> - * @throws IOException if reading from the stream fails - */ + @Override public TemplatePersistenceData[] read(InputStream stream, ResourceBundle bundle) throws IOException { - return read(new InputSource(stream), bundle, null); - } - - /** - * Reads templates from an <code>InputSource</code> and adds them to the templates. - * - * @param source the input source - * @param bundle a resource bundle to use for translating the read templates, or <code>null</code> if no translation should occur - * @param singleId the template id to extract, or <code>null</code> to read in all templates - * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code> - * @throws IOException if reading from the stream fails - */ - private TemplatePersistenceData[] read(InputSource source, ResourceBundle bundle, String singleId) throws IOException { - try { - Collection<TemplatePersistenceData> templates= new ArrayList<>(); - Set<String> ids= new HashSet<>(); - - DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance(); - DocumentBuilder parser= factory.newDocumentBuilder(); - parser.setErrorHandler(new DefaultHandler()); - Document document= parser.parse(source); - - NodeList elements= document.getElementsByTagName(TEMPLATE_ELEMENT); - - int count= elements.getLength(); - for (int i= 0; i != count; i++) { - Node node= elements.item(i); - NamedNodeMap attributes= node.getAttributes(); - - if (attributes == null) - continue; - - String id= getStringValue(attributes, ID_ATTRIBUTE, null); - if (id != null && ids.contains(id)) { - String PLUGIN_ID= "org.eclipse.jface.text"; //$NON-NLS-1$ - ILog log= Platform.getLog(Platform.getBundle(PLUGIN_ID)); - String message= NLS.bind(TemplatePersistenceMessages.getString("TemplateReaderWriter.duplicate.id"), id); //$NON-NLS-1$ - log.log(new Status(IStatus.WARNING, PLUGIN_ID, IStatus.OK, message, null)); - } else { - ids.add(id); - } - - if (singleId != null && !singleId.equals(id)) - continue; - - boolean deleted = getBooleanValue(attributes, DELETED_ATTRIBUTE, false); - - String name= getStringValue(attributes, NAME_ATTRIBUTE); - name= translateString(name, bundle); - - String description= getStringValue(attributes, DESCRIPTION_ATTRIBUTE, ""); //$NON-NLS-1$ - description= translateString(description, bundle); - - String context= getStringValue(attributes, CONTEXT_ATTRIBUTE); - - if (name == null || context == null) - throw new IOException(TemplatePersistenceMessages.getString("TemplateReaderWriter.error.missing_attribute")); //$NON-NLS-1$ - - boolean enabled = getBooleanValue(attributes, ENABLED_ATTRIBUTE, true); - boolean autoInsertable= getBooleanValue(attributes, AUTO_INSERTABLE_ATTRIBUTE, true); - - StringBuilder buffer= new StringBuilder(); - NodeList children= node.getChildNodes(); - for (int j= 0; j != children.getLength(); j++) { - String value= children.item(j).getNodeValue(); - if (value != null) - buffer.append(value); - } - String pattern= buffer.toString(); - pattern= translateString(pattern, bundle); - - Template template= new Template(name, description, context, pattern, autoInsertable); - TemplatePersistenceData data= new TemplatePersistenceData(template, enabled, id); - data.setDeleted(deleted); - - templates.add(data); - - if (singleId != null && singleId.equals(id)) - break; - } - - return templates.toArray(new TemplatePersistenceData[templates.size()]); - - } catch (ParserConfigurationException e) { - Assert.isTrue(false); - } catch (SAXException e) { - throw (IOException)new IOException("Could not read template file").initCause(e); //$NON-NLS-1$ + org.eclipse.text.templates.TemplatePersistenceData[] list= super.read(stream, bundle); + TemplatePersistenceData[] result= new TemplatePersistenceData[list.length]; + for (int i= 0; i < list.length; i++) { + result[i]= new TemplatePersistenceData(list[i]); } - - return null; // dummy + return result; } - /** - * Saves the templates as XML, encoded as UTF-8 onto the given byte stream. - * - * @param templates the templates to save - * @param stream the byte output to write the templates to in XML - * @throws IOException if writing the templates fails - */ public void save(TemplatePersistenceData[] templates, OutputStream stream) throws IOException { - save(templates, new StreamResult(stream)); + super.save(templates, stream); } - /** - * Saves the templates as XML. - * - * @param templates the templates to save - * @param writer the writer to write the templates to in XML - * @throws IOException if writing the templates fails - */ public void save(TemplatePersistenceData[] templates, Writer writer) throws IOException { - save(templates, new StreamResult(writer)); + super.save(templates, writer); } - /** - * Saves the templates as XML. - * - * @param templates the templates to save - * @param result the stream result to write to - * @throws IOException if writing the templates fails - */ - private void save(TemplatePersistenceData[] templates, StreamResult result) throws IOException { - try { - DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance(); - DocumentBuilder builder= factory.newDocumentBuilder(); - Document document= builder.newDocument(); - - Node root= document.createElement(TEMPLATE_ROOT); - document.appendChild(root); - - for (TemplatePersistenceData data : templates) { - Template template= data.getTemplate(); - - Node node= document.createElement(TEMPLATE_ELEMENT); - root.appendChild(node); - - NamedNodeMap attributes= node.getAttributes(); - - String id= data.getId(); - if (id != null) { - Attr idAttr= document.createAttribute(ID_ATTRIBUTE); - idAttr.setValue(id); - attributes.setNamedItem(idAttr); - } - - if (template != null) { - Attr name= document.createAttribute(NAME_ATTRIBUTE); - name.setValue(validateXML(template.getName())); - attributes.setNamedItem(name); - } - - if (template != null) { - Attr description= document.createAttribute(DESCRIPTION_ATTRIBUTE); - description.setValue(validateXML(template.getDescription())); - attributes.setNamedItem(description); - } - - if (template != null) { - Attr context= document.createAttribute(CONTEXT_ATTRIBUTE); - context.setValue(validateXML(template.getContextTypeId())); - attributes.setNamedItem(context); - } - - Attr enabled= document.createAttribute(ENABLED_ATTRIBUTE); - enabled.setValue(data.isEnabled() ? Boolean.toString(true) : Boolean.toString(false)); - attributes.setNamedItem(enabled); - - Attr deleted= document.createAttribute(DELETED_ATTRIBUTE); - deleted.setValue(data.isDeleted() ? Boolean.toString(true) : Boolean.toString(false)); - attributes.setNamedItem(deleted); - - if (template != null) { - Attr autoInsertable= document.createAttribute(AUTO_INSERTABLE_ATTRIBUTE); - autoInsertable.setValue(template.isAutoInsertable() ? Boolean.toString(true) : Boolean.toString(false)); - attributes.setNamedItem(autoInsertable); - } - - if (template != null) { - Text pattern= document.createTextNode(validateXML(template.getPattern())); - node.appendChild(pattern); - } - } - - - Transformer transformer=TransformerFactory.newInstance().newTransformer(); - transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ - transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$ - DOMSource source = new DOMSource(document); - - transformer.transform(source, result); - - } catch (ParserConfigurationException e) { - Assert.isTrue(false); - } catch (TransformerException e) { - if (e.getException() instanceof IOException) - throw (IOException) e.getException(); - Assert.isTrue(false); - } - } - - /** - * Validates whether the given string only contains valid XML characters. - * - * @param string the string to validate - * @return the input string - * @throws IOException when the first invalid character is detected - * @since 3.6 - */ - private static String validateXML(String string) throws IOException { - for (int i= 0; i < string.length(); i++) { - char ch= string.charAt(i); - if (!(ch == 9 || ch == 10 || ch == 13 || ch >= 32)) - throw new IOException("Character reference \"&#" + Integer.toString(ch) + "\" is an invalid XML character."); //$NON-NLS-1$ //$NON-NLS-2$ - } - return string; - } - - private boolean getBooleanValue(NamedNodeMap attributes, String attribute, boolean defaultValue) throws SAXException { - Node enabledNode= attributes.getNamedItem(attribute); - if (enabledNode == null) - return defaultValue; - else if (enabledNode.getNodeValue().equals(Boolean.toString(true))) - return true; - else if (enabledNode.getNodeValue().equals(Boolean.toString(false))) - return false; - else - throw new SAXException(TemplatePersistenceMessages.getString("TemplateReaderWriter.error.illegal_boolean_attribute")); //$NON-NLS-1$ - } - - private String getStringValue(NamedNodeMap attributes, String name) throws SAXException { - String val= getStringValue(attributes, name, null); - if (val == null) - throw new SAXException(TemplatePersistenceMessages.getString("TemplateReaderWriter.error.missing_attribute")); //$NON-NLS-1$ - return val; - } - - private String getStringValue(NamedNodeMap attributes, String name, String defaultValue) { - Node node= attributes.getNamedItem(name); - return node == null ? defaultValue : node.getNodeValue(); - } - - private String translateString(String str, ResourceBundle bundle) { - if (bundle == null) - return str; - - int idx= str.indexOf('%'); - if (idx == -1) { - return str; - } - StringBuilder buf= new StringBuilder(); - int k= 0; - while (idx != -1) { - buf.append(str.substring(k, idx)); - for (k= idx + 1; k < str.length() && !Character.isWhitespace(str.charAt(k)); k++) { - // loop - } - String key= str.substring(idx + 1, k); - buf.append(getBundleString(key, bundle)); - idx= str.indexOf('%', k); - } - buf.append(str.substring(k)); - return buf.toString(); - } - - private String getBundleString(String key, ResourceBundle bundle) { - if (bundle != null) { - try { - return bundle.getString(key); - } catch (MissingResourceException e) { - return '!' + key + '!'; - } - } - return TemplatePersistenceMessages.getString(key); // default messages - } } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateStore.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateStore.java index 0e7889b069a..2c3ab23c29a 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateStore.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateStore.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. + * Copyright (c) 2000, 2018 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -15,19 +15,19 @@ import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; -import java.util.List; import org.eclipse.core.runtime.Assert; +import org.eclipse.text.templates.ContextTypeRegistry; +import org.eclipse.text.templates.TemplatePersistenceData; +import org.eclipse.text.templates.TemplateReaderWriter; +import org.eclipse.text.templates.TemplateStoreCore; + import org.eclipse.jface.preference.IPersistentPreferenceStore; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; -import org.eclipse.jface.text.templates.ContextTypeRegistry; -import org.eclipse.jface.text.templates.Template; -import org.eclipse.jface.text.templates.TemplateException; - /** * A collection of templates. Clients may instantiate this class. In order to * load templates contributed using the <code>org.eclipse.ui.editors.templates</code> @@ -35,21 +35,16 @@ import org.eclipse.jface.text.templates.TemplateException; * * @since 3.0 */ -public class TemplateStore { - /** The stored templates. */ - private final List<TemplatePersistenceData> fTemplates= new ArrayList<>(); +public class TemplateStore extends TemplateStoreCore { /** The preference store. */ private IPreferenceStore fPreferenceStore; + /** - * The key into <code>fPreferenceStore</code> the value of which holds custom templates - * encoded as XML. - */ - private String fKey; - /** - * The context type registry, or <code>null</code> if all templates regardless - * of context type should be loaded. + * The property listener, if any is registered, <code>null</code> otherwise. + * + * @since 3.2 */ - private ContextTypeRegistry fRegistry; + private IPropertyChangeListener fPropertyListener; /** * Set to <code>true</code> if property change events should be ignored (e.g. during writing * to the preference store). @@ -57,13 +52,6 @@ public class TemplateStore { * @since 3.2 */ private boolean fIgnorePreferenceStoreChanges= false; - /** - * The property listener, if any is registered, <code>null</code> otherwise. - * - * @since 3.2 - */ - private IPropertyChangeListener fPropertyListener; - /** * Creates a new template store. @@ -74,10 +62,10 @@ public class TemplateStore { * templates */ public TemplateStore(IPreferenceStore store, String key) { + super(null, key); Assert.isNotNull(store); Assert.isNotNull(key); fPreferenceStore= store; - fKey= key; } /** @@ -92,9 +80,9 @@ public class TemplateStore { * @param key the key into <code>store</code> where to store custom * templates */ - public TemplateStore(ContextTypeRegistry registry, IPreferenceStore store, String key) { - this(store, key); - fRegistry= registry; + public TemplateStore(org.eclipse.jface.text.templates.ContextTypeRegistry registry, IPreferenceStore store, String key) { + super(registry, null, key); + fPreferenceStore= store; } /** @@ -102,8 +90,9 @@ public class TemplateStore { * * @throws IOException if loading fails. */ + @Override public void load() throws IOException { - fTemplates.clear(); + internalGetTemplates().clear(); loadContributedTemplates(); loadCustomTemplates(); } @@ -116,6 +105,7 @@ public class TemplateStore { * * @since 3.2 */ + @Override public final void startListeningForPreferenceChanges() { if (fPropertyListener == null) { fPropertyListener= new IPropertyChangeListener() { @@ -126,7 +116,7 @@ public class TemplateStore { * save operation, and clients may trigger reloading by listening to preference store * updates. */ - if (!fIgnorePreferenceStoreChanges && fKey.equals(event.getProperty())) + if (!fIgnorePreferenceStoreChanges && getKey().equals(event.getProperty())) try { load(); } catch (IOException x) { @@ -145,6 +135,7 @@ public class TemplateStore { * * @since 3.2 */ + @Override public final void stopListeningForPreferenceChanges() { if (fPropertyListener != null) { fPreferenceStore.removePropertyChangeListener(fPropertyListener); @@ -153,53 +144,14 @@ public class TemplateStore { } /** - * Handles an {@link IOException} thrown during reloading the preferences due to a preference - * store update. The default is to write to stderr. - * - * @param x the exception - * @since 3.2 - */ - protected void handleException(IOException x) { - x.printStackTrace(); - } - - /** - * Hook method to load contributed templates. Contributed templates are superseded - * by customized versions of user added templates stored in the preferences. - * <p> - * The default implementation does nothing.</p> - * - * @throws IOException if loading fails - */ - protected void loadContributedTemplates() throws IOException { - } - - /** - * Adds a template to the internal store. The added templates must have - * a unique id. - * - * @param data the template data to add - */ - protected void internalAdd(TemplatePersistenceData data) { - if (!data.isCustom()) { - // check if the added template is not a duplicate id - String id= data.getId(); - for (TemplatePersistenceData persistenceData : fTemplates) { - if (persistenceData.getId() != null && persistenceData.getId().equals(id)) - return; - } - fTemplates.add(data); - } - } - - /** * Saves the templates to the preferences. * * @throws IOException if the templates cannot be written */ + @Override public void save() throws IOException { ArrayList<TemplatePersistenceData> custom= new ArrayList<>(); - for (TemplatePersistenceData data : fTemplates) { + for (TemplatePersistenceData data : internalGetTemplates()) { if (data.isCustom() && !(data.isUserAdded() && data.isDeleted())) // don't save deleted user-added templates custom.add(data); } @@ -210,7 +162,7 @@ public class TemplateStore { fIgnorePreferenceStoreChanges= true; try { - fPreferenceStore.setValue(fKey, output.toString()); + fPreferenceStore.setValue(getKey(), output.toString()); if (fPreferenceStore instanceof IPersistentPreferenceStore) ((IPersistentPreferenceStore)fPreferenceStore).save(); } finally { @@ -219,71 +171,20 @@ public class TemplateStore { } /** - * Adds a template encapsulated in its persistent form. - * - * @param data the template to add - */ - public void add(TemplatePersistenceData data) { - - if (!validateTemplate(data.getTemplate())) - return; - - if (data.isUserAdded()) { - fTemplates.add(data); - } else { - for (TemplatePersistenceData persistenceData : fTemplates) { - if (persistenceData.getId() != null && persistenceData.getId().equals(data.getId())) { - persistenceData.setTemplate(data.getTemplate()); - persistenceData.setDeleted(data.isDeleted()); - persistenceData.setEnabled(data.isEnabled()); - return; - } - } - - // add an id which is not contributed as add-on - if (data.getTemplate() != null) { - TemplatePersistenceData newData= new TemplatePersistenceData(data.getTemplate(), data.isEnabled()); - fTemplates.add(newData); - } - } - } - - /** - * Removes a template from the store. - * - * @param data the template to remove - */ - public void delete(TemplatePersistenceData data) { - if (data.isUserAdded()) - fTemplates.remove(data); - else - data.setDeleted(true); - } - - /** - * Restores all contributed templates that have been deleted. - */ - public void restoreDeleted() { - for (TemplatePersistenceData data : fTemplates) { - if (data.isDeleted()) - data.setDeleted(false); - } - } - - /** * Deletes all user-added templates and reverts all contributed templates. * * @param doSave <code>true</code> if the store should be saved after restoring * @since 3.5 */ + @Override public void restoreDefaults(boolean doSave) { String oldValue= null; if (!doSave) - oldValue= fPreferenceStore.getString(fKey); + oldValue= fPreferenceStore.getString(getKey()); try { fIgnorePreferenceStoreChanges= true; - fPreferenceStore.setToDefault(fKey); + fPreferenceStore.setToDefault(getKey()); } finally { fIgnorePreferenceStoreChanges= false; } @@ -298,130 +199,15 @@ public class TemplateStore { if (oldValue != null) { try { fIgnorePreferenceStoreChanges= true; - fPreferenceStore.putValue(fKey, oldValue); + fPreferenceStore.putValue(getKey(), oldValue); } finally { fIgnorePreferenceStoreChanges= false; } } } - /** - * Deletes all user-added templates and reverts all contributed templates. - * <p> - * <strong>Note:</strong> the store will be saved after restoring. - * </p> - */ - public void restoreDefaults() { - restoreDefaults(true); - } - - /** - * Returns all enabled templates. - * - * @return all enabled templates - */ - public Template[] getTemplates() { - return getTemplates(null); - } - - /** - * Returns all enabled templates for the given context type. - * - * @param contextTypeId the id of the context type of the requested templates, or <code>null</code> if all templates should be returned - * @return all enabled templates for the given context type - */ - public Template[] getTemplates(String contextTypeId) { - List<Template> templates= new ArrayList<>(); - for (TemplatePersistenceData data : fTemplates) { - if (data.isEnabled() && !data.isDeleted() && (contextTypeId == null || contextTypeId.equals(data.getTemplate().getContextTypeId()))) - templates.add(data.getTemplate()); - } - - return templates.toArray(new Template[templates.size()]); - } - - /** - * Returns the first enabled template that matches the name. - * - * @param name the name of the template searched for - * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found - */ - public Template findTemplate(String name) { - return findTemplate(name, null); - } - - /** - * Returns the first enabled template that matches both name and context type id. - * - * @param name the name of the template searched for - * @param contextTypeId the context type id to clip unwanted templates, or <code>null</code> if any context type is OK - * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found - */ - public Template findTemplate(String name, String contextTypeId) { - Assert.isNotNull(name); - - for (TemplatePersistenceData data : fTemplates) { - Template template= data.getTemplate(); - if (data.isEnabled() && !data.isDeleted() - && (contextTypeId == null || contextTypeId.equals(template.getContextTypeId())) - && name.equals(template.getName())) - return template; - } - - return null; - } - - /** - * Returns the first enabled template that matches the given template id. - * - * @param id the id of the template searched for - * @return the first enabled template that matches id, or <code>null</code> if none is found - * @since 3.1 - */ - public Template findTemplateById(String id) { - TemplatePersistenceData data= getTemplateData(id); - if (data != null && !data.isDeleted()) - return data.getTemplate(); - - return null; - } - - /** - * Returns all template data. - * - * @param includeDeleted whether to include deleted data - * @return all template data, whether enabled or not - */ - public TemplatePersistenceData[] getTemplateData(boolean includeDeleted) { - List<TemplatePersistenceData> datas= new ArrayList<>(); - for (TemplatePersistenceData data : fTemplates) { - if (includeDeleted || !data.isDeleted()) - datas.add(data); - } - - return datas.toArray(new TemplatePersistenceData[datas.size()]); - } - - /** - * Returns the template data of the template with id <code>id</code> or - * <code>null</code> if no such template can be found. - * - * @param id the id of the template data - * @return the template data of the template with id <code>id</code> or <code>null</code> - * @since 3.1 - */ - public TemplatePersistenceData getTemplateData(String id) { - Assert.isNotNull(id); - for (TemplatePersistenceData data : fTemplates) { - if (id.equals(data.getId())) - return data; - } - - return null; - } - private void loadCustomTemplates() throws IOException { - String pref= fPreferenceStore.getString(fKey); + String pref= fPreferenceStore.getString(getKey()); if (pref != null && pref.trim().length() > 0) { Reader input= new StringReader(pref); TemplateReaderWriter reader= new TemplateReaderWriter(); @@ -432,51 +218,42 @@ public class TemplateStore { } } - /** - * Validates a template against the context type registered in the context - * type registry. Returns always <code>true</code> if no registry is - * present. - * - * @param template the template to validate - * @return <code>true</code> if validation is successful or no context - * type registry is specified, <code>false</code> if validation - * fails - */ - private boolean validateTemplate(Template template) { - String contextTypeId= template.getContextTypeId(); - if (contextExists(contextTypeId)) { - if (fRegistry != null) - try { - fRegistry.getContextType(contextTypeId).validate(template.getPattern()); - } catch (TemplateException e) { - return false; - } - return true; - } + @Override + protected final org.eclipse.jface.text.templates.ContextTypeRegistry getRegistry() { + ContextTypeRegistry registry= super.getRegistry(); + org.eclipse.jface.text.templates.ContextTypeRegistry res= new org.eclipse.jface.text.templates.ContextTypeRegistry(); + registry.contextTypes().forEachRemaining(t -> res.addContextType(t)); + return res; + } - return false; + public void add(org.eclipse.jface.text.templates.persistence.TemplatePersistenceData data) { + super.add(data); } - /** - * Returns <code>true</code> if a context type id specifies a valid context type - * or if no context type registry is present. - * - * @param contextTypeId the context type id to look for - * @return <code>true</code> if the context type specified by the id - * is present in the context type registry, or if no registry is - * specified - */ - private boolean contextExists(String contextTypeId) { - return contextTypeId != null && (fRegistry == null || fRegistry.getContextType(contextTypeId) != null); + public void delete(org.eclipse.jface.text.templates.persistence.TemplatePersistenceData data) { + super.delete(data); } - /** - * Returns the registry. - * - * @return Returns the registry - */ - protected final ContextTypeRegistry getRegistry() { - return fRegistry; + @Override + public org.eclipse.jface.text.templates.persistence.TemplatePersistenceData[] getTemplateData(boolean includeDeleted) { + TemplatePersistenceData[] list= super.getTemplateData(includeDeleted); + org.eclipse.jface.text.templates.persistence.TemplatePersistenceData[] wraps= new org.eclipse.jface.text.templates.persistence.TemplatePersistenceData[list.length]; + for (int i= 0; i < wraps.length; i++) { + wraps[i]= new org.eclipse.jface.text.templates.persistence.TemplatePersistenceData(list[i]); + } + return wraps; + } + + @Override + public org.eclipse.jface.text.templates.persistence.TemplatePersistenceData getTemplateData(String id) { + TemplatePersistenceData data= super.getTemplateData(id); + org.eclipse.jface.text.templates.persistence.TemplatePersistenceData wrap= new org.eclipse.jface.text.templates.persistence.TemplatePersistenceData(data); + return wrap; } + + protected void internalAdd(org.eclipse.jface.text.templates.persistence.TemplatePersistenceData data) { + super.internalAdd(data); + } + } diff --git a/org.eclipse.text/META-INF/MANIFEST.MF b/org.eclipse.text/META-INF/MANIFEST.MF index bf2f9bd7c76..b10ea24cce9 100644 --- a/org.eclipse.text/META-INF/MANIFEST.MF +++ b/org.eclipse.text/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.text -Bundle-Version: 3.6.400.qualifier +Bundle-Version: 3.7.0.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: @@ -12,10 +12,13 @@ Export-Package: org.eclipse.jface.text.source; text="split"; mandatory:="text", org.eclipse.jface.text.templates; text="split"; mandatory:="text", org.eclipse.text.edits, + org.eclipse.text.templates, org.eclipse.text.undo Require-Bundle: org.eclipse.core.commands;bundle-version="[3.5.0,4.0.0)", - org.eclipse.equinox.common;bundle-version="[3.5.0,4.0.0)" + org.eclipse.equinox.common;bundle-version="[3.5.0,4.0.0)", + org.eclipse.equinox.preferences;bundle-version="[3.7.0,4.0.0)", + org.eclipse.core.runtime;bundle-version="[3.14.0,4.0.0)" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: com.ibm.icu.text, diff --git a/org.eclipse.text/pom.xml b/org.eclipse.text/pom.xml index 2cfcce0c604..cb23642394d 100644 --- a/org.eclipse.text/pom.xml +++ b/org.eclipse.text/pom.xml @@ -18,6 +18,6 @@ </parent> <groupId>org.eclipse.text</groupId> <artifactId>org.eclipse.text</artifactId> - <version>3.6.400-SNAPSHOT</version> + <version>3.7.0-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/org.eclipse.text/src/org/eclipse/text/templates/ContextTypeRegistry.java b/org.eclipse.text/src/org/eclipse/text/templates/ContextTypeRegistry.java new file mode 100644 index 00000000000..a966cae545e --- /dev/null +++ b/org.eclipse.text/src/org/eclipse/text/templates/ContextTypeRegistry.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.text.templates; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jface.text.templates.TemplateContextType; + +/** + * A registry for context types. Editor implementors will usually instantiate a + * registry and configure the context types available in their editor. + * <p> + * In order to pick up templates contributed using the <code>org.eclipse.ui.editors.templates</code> + * extension point, use a <code>ContributionContextTypeRegistry</code>. + * </p> + * + * @since 3.7 + */ +public class ContextTypeRegistry { + + /** all known context types */ + private final Map<String, TemplateContextType> fContextTypes= new LinkedHashMap<>(); + + /** + * Adds a context type to the registry. If there already is a context type + * with the same ID registered, it is replaced. + * + * @param contextType the context type to add + */ + public void addContextType(TemplateContextType contextType) { + fContextTypes.put(contextType.getId(), contextType); + } + + /** + * Returns the context type if the id is valid, <code>null</code> otherwise. + * + * @param id the id of the context type to retrieve + * @return the context type if <code>name</code> is valid, <code>null</code> otherwise + */ + public TemplateContextType getContextType(String id) { + return fContextTypes.get(id); + } + + /** + * Returns an iterator over all registered context types. + * + * @return an iterator over all registered context types + */ + public Iterator<TemplateContextType> contextTypes() { + return fContextTypes.values().iterator(); + } +} diff --git a/org.eclipse.text/src/org/eclipse/text/templates/TemplatePersistenceData.java b/org.eclipse.text/src/org/eclipse/text/templates/TemplatePersistenceData.java new file mode 100644 index 00000000000..60dfc06f2a0 --- /dev/null +++ b/org.eclipse.text/src/org/eclipse/text/templates/TemplatePersistenceData.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.text.templates; + +import org.eclipse.core.runtime.Assert; + +import org.eclipse.jface.text.templates.Template; + + +/** + * TemplatePersistenceData stores information about a template. It uniquely + * references contributed templates via their id. Contributed templates may be + * deleted or modified. All template may be enabled or not. + * <p> + * Clients may use this class, although this is not usually needed except when + * implementing a custom template preference page or template store. This class + * is not intended to be subclassed. + * </p> + * + * @since 3.7 + * @noextend This class is not intended to be subclassed by clients. + */ +public class TemplatePersistenceData { + private final Template fOriginalTemplate; + private final String fId; + private final boolean fOriginalIsEnabled; + + private Template fCustomTemplate= null; + private boolean fIsDeleted= false; + private boolean fCustomIsEnabled= true; + + /** + * Creates a new, user-added instance that is not linked to a contributed + * template. + * + * @param template the template which is stored by the new instance + * @param enabled whether the template is enabled + */ + public TemplatePersistenceData(Template template, boolean enabled) { + this(template, enabled, null); + } + + /** + * Creates a new instance. If <code>id</code> is not <code>null</code>, + * the instance is represents a template that is contributed and can be + * identified via its id. + * + * @param template the template which is stored by the new instance + * @param enabled whether the template is enabled + * @param id the id of the template, or <code>null</code> if a user-added + * instance should be created + */ + public TemplatePersistenceData(Template template, boolean enabled, String id) { + Assert.isNotNull(template); + fOriginalTemplate= template; + fCustomTemplate= template; + fOriginalIsEnabled= enabled; + fCustomIsEnabled= enabled; + fId= id; + } + + /** + * Returns the id of this template store, or <code>null</code> if there is none. + * + * @return the id of this template store + */ + public String getId() { + return fId; + } + + /** + * Returns the deletion state of the stored template. This is only relevant + * of contributed templates. + * + * @return the deletion state of the stored template + */ + public boolean isDeleted() { + return fIsDeleted; + } + + /** + * Sets the deletion state of the stored template. + * + * @param isDeleted the deletion state of the stored template + */ + public void setDeleted(boolean isDeleted) { + fIsDeleted= isDeleted; + } + + /** + * Returns the template encapsulated by the receiver. + * + * @return the template encapsulated by the receiver + */ + public Template getTemplate() { + return fCustomTemplate; + } + + + /** + * Sets the template encapsulated by the receiver. + * + * @param template the new template + */ + public void setTemplate(Template template) { + fCustomTemplate= template; + } + + /** + * Returns whether the receiver represents a custom template, i.e. is either + * a user-added template or a contributed template that has been modified. + * + * @return <code>true</code> if the contained template is a custom + * template and cannot be reconstructed from the contributed + * templates + */ + public boolean isCustom() { + return fId == null + || fIsDeleted + || fOriginalIsEnabled != fCustomIsEnabled + || !fOriginalTemplate.equals(fCustomTemplate); + } + + /** + * Returns whether the receiver represents a modified template, i.e. a + * contributed template that has been changed. + * + * @return <code>true</code> if the contained template is contributed but has been modified, <code>false</code> otherwise + */ + public boolean isModified() { + return isCustom() && !isUserAdded(); + } + + /** + * Returns <code>true</code> if the contained template was added by a + * user, i.e. does not reference a contributed template. + * + * @return <code>true</code> if the contained template was added by a user, <code>false</code> otherwise + */ + public boolean isUserAdded() { + return fId == null; + } + + + /** + * Reverts the template to its original setting. + */ + public void revert() { + fCustomTemplate= fOriginalTemplate; + fCustomIsEnabled= fOriginalIsEnabled; + fIsDeleted= false; + } + + + /** + * Returns the enablement state of the contained template. + * + * @return the enablement state of the contained template + */ + public boolean isEnabled() { + return fCustomIsEnabled; + } + + /** + * Sets the enablement state of the contained template. + * + * @param isEnabled the new enablement state of the contained template + */ + public void setEnabled(boolean isEnabled) { + fCustomIsEnabled= isEnabled; + } +} diff --git a/org.eclipse.text/src/org/eclipse/text/templates/TemplateReaderWriter.java b/org.eclipse.text/src/org/eclipse/text/templates/TemplateReaderWriter.java new file mode 100644 index 00000000000..3abeec0dbc2 --- /dev/null +++ b/org.eclipse.text/src/org/eclipse/text/templates/TemplateReaderWriter.java @@ -0,0 +1,414 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.text.templates; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + +import org.eclipse.osgi.util.NLS; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; + +import org.eclipse.jface.text.templates.Template; + +/** + * Serializes templates as character or byte stream and reads the same format + * back. + * <p> + * Clients may instantiate this class, it is not intended to be + * subclassed.</p> + * + * @since 3.7 + * @noextend This class is not intended to be subclassed by clients. + */ +public class TemplateReaderWriter { + + private static final String TEMPLATE_ROOT = "templates"; //$NON-NLS-1$ + private static final String TEMPLATE_ELEMENT = "template"; //$NON-NLS-1$ + private static final String NAME_ATTRIBUTE= "name"; //$NON-NLS-1$ + private static final String ID_ATTRIBUTE= "id"; //$NON-NLS-1$ + private static final String DESCRIPTION_ATTRIBUTE= "description"; //$NON-NLS-1$ + private static final String CONTEXT_ATTRIBUTE= "context"; //$NON-NLS-1$ + private static final String ENABLED_ATTRIBUTE= "enabled"; //$NON-NLS-1$ + private static final String DELETED_ATTRIBUTE= "deleted"; //$NON-NLS-1$ + /** + * @since 3.1 + */ + private static final String AUTO_INSERTABLE_ATTRIBUTE= "autoinsert"; //$NON-NLS-1$ + + /** + * Create a new instance. + */ + public TemplateReaderWriter() { + } + + /** + * Reads templates from a reader and returns them. The reader must present + * a serialized form as produced by the <code>save</code> method. + * + * @param reader the reader to read templates from + * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code> + * @throws IOException if reading from the stream fails + */ + public TemplatePersistenceData[] read(Reader reader) throws IOException { + return read(reader, null); + } + + /** + * Reads the template with identifier <code>id</code> from a reader and + * returns it. The reader must present a serialized form as produced by the + * <code>save</code> method. + * + * @param reader the reader to read templates from + * @param id the id of the template to return + * @return the read template, encapsulated in an instances of + * <code>TemplatePersistenceData</code> + * @throws IOException if reading from the stream fails + * @since 3.1 + */ + public TemplatePersistenceData readSingle(Reader reader, String id) throws IOException { + TemplatePersistenceData[] datas= read(new InputSource(reader), null, id); + if (datas.length > 0) + return datas[0]; + return null; + } + + /** + * Reads templates from a stream and adds them to the templates. + * + * @param reader the reader to read templates from + * @param bundle a resource bundle to use for translating the read templates, or <code>null</code> if no translation should occur + * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code> + * @throws IOException if reading from the stream fails + */ + public TemplatePersistenceData[] read(Reader reader, ResourceBundle bundle) throws IOException { + return read(new InputSource(reader), bundle, null); + } + + /** + * Reads templates from a stream and adds them to the templates. + * + * @param stream the byte stream to read templates from + * @param bundle a resource bundle to use for translating the read templates, or <code>null</code> if no translation should occur + * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code> + * @throws IOException if reading from the stream fails + */ + public TemplatePersistenceData[] read(InputStream stream, ResourceBundle bundle) throws IOException { + return read(new InputSource(stream), bundle, null); + } + + /** + * Reads templates from an <code>InputSource</code> and adds them to the templates. + * + * @param source the input source + * @param bundle a resource bundle to use for translating the read templates, or <code>null</code> if no translation should occur + * @param singleId the template id to extract, or <code>null</code> to read in all templates + * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code> + * @throws IOException if reading from the stream fails + */ + private TemplatePersistenceData[] read(InputSource source, ResourceBundle bundle, String singleId) throws IOException { + try { + Collection<TemplatePersistenceData> templates= new ArrayList<>(); + Set<String> ids= new HashSet<>(); + + DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance(); + DocumentBuilder parser= factory.newDocumentBuilder(); + parser.setErrorHandler(new DefaultHandler()); + Document document= parser.parse(source); + + NodeList elements= document.getElementsByTagName(TEMPLATE_ELEMENT); + + int count= elements.getLength(); + for (int i= 0; i != count; i++) { + Node node= elements.item(i); + NamedNodeMap attributes= node.getAttributes(); + + if (attributes == null) + continue; + + String id= getStringValue(attributes, ID_ATTRIBUTE, null); + if (id != null && ids.contains(id)) { + String PLUGIN_ID= "org.eclipse.jface.text"; //$NON-NLS-1$ + ILog log= Platform.getLog(Platform.getBundle(PLUGIN_ID)); + String message= NLS.bind(TextTemplateMessages.getString("TemplateReaderWriter.duplicate.id"), id); //$NON-NLS-1$ + log.log(new Status(IStatus.WARNING, PLUGIN_ID, IStatus.OK, message, null)); + } else { + ids.add(id); + } + + if (singleId != null && !singleId.equals(id)) + continue; + + boolean deleted = getBooleanValue(attributes, DELETED_ATTRIBUTE, false); + + String name= getStringValue(attributes, NAME_ATTRIBUTE); + name= translateString(name, bundle); + + String description= getStringValue(attributes, DESCRIPTION_ATTRIBUTE, ""); //$NON-NLS-1$ + description= translateString(description, bundle); + + String context= getStringValue(attributes, CONTEXT_ATTRIBUTE); + + if (name == null || context == null) + throw new IOException(TextTemplateMessages.getString("TemplateReaderWriter.error.missing_attribute")); //$NON-NLS-1$ + + boolean enabled = getBooleanValue(attributes, ENABLED_ATTRIBUTE, true); + boolean autoInsertable= getBooleanValue(attributes, AUTO_INSERTABLE_ATTRIBUTE, true); + + StringBuilder buffer= new StringBuilder(); + NodeList children= node.getChildNodes(); + for (int j= 0; j != children.getLength(); j++) { + String value= children.item(j).getNodeValue(); + if (value != null) + buffer.append(value); + } + String pattern= buffer.toString(); + pattern= translateString(pattern, bundle); + + Template template= new Template(name, description, context, pattern, autoInsertable); + TemplatePersistenceData data= new TemplatePersistenceData(template, enabled, id); + data.setDeleted(deleted); + + templates.add(data); + + if (singleId != null && singleId.equals(id)) + break; + } + + return templates.toArray(new TemplatePersistenceData[templates.size()]); + + } catch (ParserConfigurationException e) { + Assert.isTrue(false); + } catch (SAXException e) { + throw (IOException)new IOException("Could not read template file").initCause(e); //$NON-NLS-1$ + } + + return null; // dummy + } + + /** + * Saves the templates as XML, encoded as UTF-8 onto the given byte stream. + * + * @param templates the templates to save + * @param stream the byte output to write the templates to in XML + * @throws IOException if writing the templates fails + */ + public void save(TemplatePersistenceData[] templates, OutputStream stream) throws IOException { + save(templates, new StreamResult(stream)); + } + + /** + * Saves the templates as XML. + * + * @param templates the templates to save + * @param writer the writer to write the templates to in XML + * @throws IOException if writing the templates fails + */ + public void save(TemplatePersistenceData[] templates, Writer writer) throws IOException { + save(templates, new StreamResult(writer)); + } + + /** + * Saves the templates as XML. + * + * @param templates the templates to save + * @param result the stream result to write to + * @throws IOException if writing the templates fails + */ + private void save(TemplatePersistenceData[] templates, StreamResult result) throws IOException { + try { + DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance(); + DocumentBuilder builder= factory.newDocumentBuilder(); + Document document= builder.newDocument(); + + Node root= document.createElement(TEMPLATE_ROOT); + document.appendChild(root); + + for (TemplatePersistenceData data : templates) { + Template template= data.getTemplate(); + + Node node= document.createElement(TEMPLATE_ELEMENT); + root.appendChild(node); + + NamedNodeMap attributes= node.getAttributes(); + + String id= data.getId(); + if (id != null) { + Attr idAttr= document.createAttribute(ID_ATTRIBUTE); + idAttr.setValue(id); + attributes.setNamedItem(idAttr); + } + + if (template != null) { + Attr name= document.createAttribute(NAME_ATTRIBUTE); + name.setValue(validateXML(template.getName())); + attributes.setNamedItem(name); + } + + if (template != null) { + Attr description= document.createAttribute(DESCRIPTION_ATTRIBUTE); + description.setValue(validateXML(template.getDescription())); + attributes.setNamedItem(description); + } + + if (template != null) { + Attr context= document.createAttribute(CONTEXT_ATTRIBUTE); + context.setValue(validateXML(template.getContextTypeId())); + attributes.setNamedItem(context); + } + + Attr enabled= document.createAttribute(ENABLED_ATTRIBUTE); + enabled.setValue(data.isEnabled() ? Boolean.toString(true) : Boolean.toString(false)); + attributes.setNamedItem(enabled); + + Attr deleted= document.createAttribute(DELETED_ATTRIBUTE); + deleted.setValue(data.isDeleted() ? Boolean.toString(true) : Boolean.toString(false)); + attributes.setNamedItem(deleted); + + if (template != null) { + Attr autoInsertable= document.createAttribute(AUTO_INSERTABLE_ATTRIBUTE); + autoInsertable.setValue(template.isAutoInsertable() ? Boolean.toString(true) : Boolean.toString(false)); + attributes.setNamedItem(autoInsertable); + } + + if (template != null) { + Text pattern= document.createTextNode(validateXML(template.getPattern())); + node.appendChild(pattern); + } + } + + + Transformer transformer=TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$ + DOMSource source = new DOMSource(document); + + transformer.transform(source, result); + + } catch (ParserConfigurationException e) { + Assert.isTrue(false); + } catch (TransformerException e) { + if (e.getException() instanceof IOException) + throw (IOException) e.getException(); + Assert.isTrue(false); + } + } + + /** + * Validates whether the given string only contains valid XML characters. + * + * @param string the string to validate + * @return the input string + * @throws IOException when the first invalid character is detected + * @since 3.6 + */ + private static String validateXML(String string) throws IOException { + for (int i= 0; i < string.length(); i++) { + char ch= string.charAt(i); + if (!(ch == 9 || ch == 10 || ch == 13 || ch >= 32)) + throw new IOException("Character reference \"&#" + Integer.toString(ch) + "\" is an invalid XML character."); //$NON-NLS-1$ //$NON-NLS-2$ + } + return string; + } + + private boolean getBooleanValue(NamedNodeMap attributes, String attribute, boolean defaultValue) throws SAXException { + Node enabledNode= attributes.getNamedItem(attribute); + if (enabledNode == null) + return defaultValue; + else if (enabledNode.getNodeValue().equals(Boolean.toString(true))) + return true; + else if (enabledNode.getNodeValue().equals(Boolean.toString(false))) + return false; + else + throw new SAXException(TextTemplateMessages.getString("TemplateReaderWriter.error.illegal_boolean_attribute")); //$NON-NLS-1$ + } + + private String getStringValue(NamedNodeMap attributes, String name) throws SAXException { + String val= getStringValue(attributes, name, null); + if (val == null) + throw new SAXException(TextTemplateMessages.getString("TemplateReaderWriter.error.missing_attribute")); //$NON-NLS-1$ + return val; + } + + private String getStringValue(NamedNodeMap attributes, String name, String defaultValue) { + Node node= attributes.getNamedItem(name); + return node == null ? defaultValue : node.getNodeValue(); + } + + private String translateString(String str, ResourceBundle bundle) { + if (bundle == null) + return str; + + int idx= str.indexOf('%'); + if (idx == -1) { + return str; + } + StringBuilder buf= new StringBuilder(); + int k= 0; + while (idx != -1) { + buf.append(str.substring(k, idx)); + for (k= idx + 1; k < str.length() && !Character.isWhitespace(str.charAt(k)); k++) { + // loop + } + String key= str.substring(idx + 1, k); + buf.append(getBundleString(key, bundle)); + idx= str.indexOf('%', k); + } + buf.append(str.substring(k)); + return buf.toString(); + } + + private String getBundleString(String key, ResourceBundle bundle) { + if (bundle != null) { + try { + return bundle.getString(key); + } catch (MissingResourceException e) { + return '!' + key + '!'; + } + } + return TextTemplateMessages.getString(key); // default messages + } +} + diff --git a/org.eclipse.text/src/org/eclipse/text/templates/TemplateStoreCore.java b/org.eclipse.text/src/org/eclipse/text/templates/TemplateStoreCore.java new file mode 100644 index 00000000000..a83edce2d7c --- /dev/null +++ b/org.eclipse.text/src/org/eclipse/text/templates/TemplateStoreCore.java @@ -0,0 +1,504 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.text.templates; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import org.osgi.service.prefs.BackingStoreException; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; + +import org.eclipse.text.templates.ContextTypeRegistry; +import org.eclipse.text.templates.TemplatePersistenceData; + +import org.eclipse.jface.text.templates.Template; +import org.eclipse.jface.text.templates.TemplateException; + +/** + * A collection of templates. Clients may instantiate this class. In order to + * load templates contributed using the <code>org.eclipse.ui.editors.templates</code> + * extension point, use a <code>ContributionTemplateStore</code>. + * + * @since 3.7 + */ +public class TemplateStoreCore { + + /** The stored templates. */ + private final List<TemplatePersistenceData> fTemplates= new ArrayList<>(); + /** The preference store. */ + private IEclipsePreferences fPreferenceStore; + /** + * The key into <code>fPreferenceStore</code> the value of which holds custom templates + * encoded as XML. + */ + private String fKey; + /** + * The context type registry, or <code>null</code> if all templates regardless + * of context type should be loaded. + */ + private ContextTypeRegistry fRegistry; + /** + * Set to <code>true</code> if property change events should be ignored (e.g. during writing + * to the preference store). + * + * @since 3.2 + */ + private boolean fIgnorePreferenceStoreChanges= false; + /** + * The property listener, if any is registered, <code>null</code> otherwise. + * + * @since 3.2 + */ + private IPreferenceChangeListener fPropertyListener; + + + /** + * Creates a new template store. + * + * @param store the preference store in which to store custom templates + * under <code>key</code> + * @param key the key into <code>store</code> where to store custom + * templates + */ + public TemplateStoreCore(IEclipsePreferences store, String key) { + Assert.isNotNull(key); + fPreferenceStore= store; + fKey= key; + } + + /** + * Creates a new template store with a context type registry. Only templates + * that specify a context type contained in the registry will be loaded by + * this store if the registry is not <code>null</code>. + * + * @param registry a context type registry, or <code>null</code> if all + * templates should be loaded + * @param store the preference store in which to store custom templates + * under <code>key</code> + * @param key the key into <code>store</code> where to store custom + * templates + */ + public TemplateStoreCore(ContextTypeRegistry registry, IEclipsePreferences store, String key) { + this(store, key); + fRegistry= registry; + } + + /** + * Loads the templates from contributions and preferences. + * + * @throws IOException if loading fails. + */ + public void load() throws IOException { + fTemplates.clear(); + loadContributedTemplates(); + loadCustomTemplates(); + } + + /** + * Starts listening for property changes on the preference store. If the configured preference + * key changes, the template store is {@link #load() reloaded}. Call + * {@link #stopListeningForPreferenceChanges()} to remove any listener and stop the + * auto-updating behavior. + * + * @since 3.2 + */ + public void startListeningForPreferenceChanges() { + if (fPropertyListener == null) { + fPropertyListener= new IPreferenceChangeListener() { + @Override + public void preferenceChange(PreferenceChangeEvent event) { + /* + * Don't load if we are in the process of saving ourselves. We are in sync anyway after the + * save operation, and clients may trigger reloading by listening to preference store + * updates. + */ + if (!fIgnorePreferenceStoreChanges && fKey.equals(event.getKey())) + try { + load(); + } catch (IOException x) { + handleException(x); + } + } + }; + fPreferenceStore.addPreferenceChangeListener(fPropertyListener); + } + + } + + /** + * Stops the auto-updating behavior started by calling + * {@link #startListeningForPreferenceChanges()}. + * + * @since 3.2 + */ + public void stopListeningForPreferenceChanges() { + if (fPropertyListener != null) { + fPreferenceStore.removePreferenceChangeListener(fPropertyListener); + fPropertyListener= null; + } + } + + /** + * Handles an {@link IOException} thrown during reloading the preferences due to a preference + * store update. The default is to write to stderr. + * + * @param x the exception + * @since 3.2 + */ + protected void handleException(IOException x) { + x.printStackTrace(); + } + + /** + * Hook method to load contributed templates. Contributed templates are superseded + * by customized versions of user added templates stored in the preferences. + * <p> + * The default implementation does nothing.</p> + * + * @throws IOException if loading fails + */ + protected void loadContributedTemplates() throws IOException { + } + + /** + * Adds a template to the internal store. The added templates must have + * a unique id. + * + * @param data the template data to add + */ + protected void internalAdd(TemplatePersistenceData data) { + if (!data.isCustom()) { + // check if the added template is not a duplicate id + String id= data.getId(); + for (TemplatePersistenceData persistenceData : fTemplates) { + if (persistenceData.getId() != null && persistenceData.getId().equals(id)) + return; + } + fTemplates.add(data); + } + } + + /** + * Saves the templates to the preferences. + * + * @throws IOException if the templates cannot be written + */ + public void save() throws IOException { + ArrayList<TemplatePersistenceData> custom= new ArrayList<>(); + for (TemplatePersistenceData data : fTemplates) { + if (data.isCustom() && !(data.isUserAdded() && data.isDeleted())) // don't save deleted user-added templates + custom.add(data); + } + + StringWriter output= new StringWriter(); + TemplateReaderWriter writer= new TemplateReaderWriter(); + writer.save(custom.toArray(new TemplatePersistenceData[custom.size()]), output); + + fIgnorePreferenceStoreChanges= true; + try { + fPreferenceStore.put(fKey, output.toString()); + fPreferenceStore.flush(); + } catch (BackingStoreException e) { + } finally { + fIgnorePreferenceStoreChanges= false; + } + } + + /** + * Adds a template encapsulated in its persistent form. + * + * @param data the template to add + */ + public void add(TemplatePersistenceData data) { + + if (!validateTemplate(data.getTemplate())) + return; + + if (data.isUserAdded()) { + fTemplates.add(data); + } else { + for (TemplatePersistenceData persistenceData : fTemplates) { + if (persistenceData.getId() != null && persistenceData.getId().equals(data.getId())) { + persistenceData.setTemplate(data.getTemplate()); + persistenceData.setDeleted(data.isDeleted()); + persistenceData.setEnabled(data.isEnabled()); + return; + } + } + + // add an id which is not contributed as add-on + if (data.getTemplate() != null) { + TemplatePersistenceData newData= new TemplatePersistenceData(data.getTemplate(), data.isEnabled(), data.getId()); + fTemplates.add(newData); + } + } + } + + /** + * Removes a template from the store. + * + * @param data the template to remove + */ + public void delete(TemplatePersistenceData data) { + if (data.isUserAdded()) + fTemplates.remove(data); + else + data.setDeleted(true); + } + + /** + * Restores all contributed templates that have been deleted. + */ + public void restoreDeleted() { + for (TemplatePersistenceData data : fTemplates) { + if (data.isDeleted()) + data.setDeleted(false); + } + } + + /** + * Deletes all user-added templates and reverts all contributed templates. + * + * @param doSave <code>true</code> if the store should be saved after restoring + * @since 3.5 + */ + public void restoreDefaults(boolean doSave) { + String oldValue= null; + if (!doSave) + oldValue= fPreferenceStore.get(fKey, null); + + try { + fIgnorePreferenceStoreChanges= true; + // See IPreferenceStore for default String value + fPreferenceStore.put(fKey, ""); //$NON-NLS-1$ + } finally { + fIgnorePreferenceStoreChanges= false; + } + + try { + load(); + } catch (IOException x) { + // can't log from jface-text + handleException(x); + } + + if (oldValue != null) { + try { + fIgnorePreferenceStoreChanges= true; + fPreferenceStore.put(fKey, oldValue); + } finally { + fIgnorePreferenceStoreChanges= false; + } + } + } + + /** + * Deletes all user-added templates and reverts all contributed templates. + * <p> + * <strong>Note:</strong> the store will be saved after restoring. + * </p> + */ + public void restoreDefaults() { + restoreDefaults(true); + } + + /** + * Returns all enabled templates. + * + * @return all enabled templates + */ + public Template[] getTemplates() { + return getTemplates(null); + } + + /** + * Returns all enabled templates for the given context type. + * + * @param contextTypeId the id of the context type of the requested templates, or <code>null</code> if all templates should be returned + * @return all enabled templates for the given context type + */ + public Template[] getTemplates(String contextTypeId) { + List<Template> templates= new ArrayList<>(); + for (TemplatePersistenceData data : fTemplates) { + if (data.isEnabled() && !data.isDeleted() && (contextTypeId == null || contextTypeId.equals(data.getTemplate().getContextTypeId()))) + templates.add(data.getTemplate()); + } + + return templates.toArray(new Template[templates.size()]); + } + + /** + * Returns the first enabled template that matches the name. + * + * @param name the name of the template searched for + * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found + */ + public Template findTemplate(String name) { + return findTemplate(name, null); + } + + /** + * Returns the first enabled template that matches both name and context type id. + * + * @param name the name of the template searched for + * @param contextTypeId the context type id to clip unwanted templates, or <code>null</code> if any context type is OK + * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found + */ + public Template findTemplate(String name, String contextTypeId) { + Assert.isNotNull(name); + + for (TemplatePersistenceData data : fTemplates) { + Template template= data.getTemplate(); + if (data.isEnabled() && !data.isDeleted() + && (contextTypeId == null || contextTypeId.equals(template.getContextTypeId())) + && name.equals(template.getName())) + return template; + } + + return null; + } + + /** + * Returns the first enabled template that matches the given template id. + * + * @param id the id of the template searched for + * @return the first enabled template that matches id, or <code>null</code> if none is found + * @since 3.1 + */ + public Template findTemplateById(String id) { + TemplatePersistenceData data= getTemplateData(id); + if (data != null && !data.isDeleted()) + return data.getTemplate(); + + return null; + } + + /** + * Returns all template data. + * + * @param includeDeleted whether to include deleted data + * @return all template data, whether enabled or not + */ + public TemplatePersistenceData[] getTemplateData(boolean includeDeleted) { + List<TemplatePersistenceData> datas= new ArrayList<>(); + for (TemplatePersistenceData data : fTemplates) { + if (includeDeleted || !data.isDeleted()) + datas.add(data); + } + + return datas.toArray(new TemplatePersistenceData[datas.size()]); + } + + /** + * Returns the template data of the template with id <code>id</code> or + * <code>null</code> if no such template can be found. + * + * @param id the id of the template data + * @return the template data of the template with id <code>id</code> or <code>null</code> + * @since 3.1 + */ + public TemplatePersistenceData getTemplateData(String id) { + Assert.isNotNull(id); + for (TemplatePersistenceData data : fTemplates) { + if (id.equals(data.getId())) + return data; + } + + return null; + } + + private void loadCustomTemplates() throws IOException { + String pref= fPreferenceStore.get(fKey, null); + if (pref != null && pref.trim().length() > 0) { + Reader input= new StringReader(pref); + TemplateReaderWriter reader= new TemplateReaderWriter(); + TemplatePersistenceData[] datas= reader.read(input); + for (TemplatePersistenceData data : datas) { + add(data); + } + } + } + + /** + * Validates a template against the context type registered in the context + * type registry. Returns always <code>true</code> if no registry is + * present. + * + * @param template the template to validate + * @return <code>true</code> if validation is successful or no context + * type registry is specified, <code>false</code> if validation + * fails + */ + private boolean validateTemplate(Template template) { + String contextTypeId= template.getContextTypeId(); + if (contextExists(contextTypeId)) { + if (fRegistry != null) + try { + fRegistry.getContextType(contextTypeId).validate(template.getPattern()); + } catch (TemplateException e) { + return false; + } + return true; + } + + return false; + } + + /** + * Returns <code>true</code> if a context type id specifies a valid context type + * or if no context type registry is present. + * + * @param contextTypeId the context type id to look for + * @return <code>true</code> if the context type specified by the id + * is present in the context type registry, or if no registry is + * specified + */ + private boolean contextExists(String contextTypeId) { + return contextTypeId != null && (fRegistry == null || fRegistry.getContextType(contextTypeId) != null); + } + + /** + * Returns the registry. + * + * @return Returns the registry + */ + protected ContextTypeRegistry getRegistry() { + return fRegistry; + } + + /** + * Return the key into the Preference Store whose value contains + * the custom templates encoded as XML. + * + * @return the key in the Preference Store + */ + protected final String getKey () { + return fKey; + } + + /** + * Return the stored templates + * @return the stored templates + */ + protected final List<TemplatePersistenceData> internalGetTemplates () { + return fTemplates; + } + +} + diff --git a/org.eclipse.text/src/org/eclipse/text/templates/TextTemplateMessages.java b/org.eclipse.text/src/org/eclipse/text/templates/TextTemplateMessages.java new file mode 100644 index 00000000000..2d032891f94 --- /dev/null +++ b/org.eclipse.text/src/org/eclipse/text/templates/TextTemplateMessages.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.text.templates; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import com.ibm.icu.text.MessageFormat; + +/* + * @since 3.0 + */ +class TextTemplateMessages { + + private static final String RESOURCE_BUNDLE= TextTemplateMessages.class.getName(); + private static ResourceBundle fgResourceBundle= ResourceBundle.getBundle(RESOURCE_BUNDLE); + + private TextTemplateMessages() { + } + + public static String getString(String key) { + try { + return fgResourceBundle.getString(key); + } catch (MissingResourceException e) { + return '!' + key + '!'; + } + } + + public static String getFormattedString(String key, Object arg) { + return MessageFormat.format(getString(key), new Object[] { arg }); + } + + + public static String getFormattedString(String key, Object[] args) { + return MessageFormat.format(getString(key), args); + } +} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplatePersistenceMessages.properties b/org.eclipse.text/src/org/eclipse/text/templates/TextTemplateMessages.properties index 51e4947ea7c..51e4947ea7c 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplatePersistenceMessages.properties +++ b/org.eclipse.text/src/org/eclipse/text/templates/TextTemplateMessages.properties |