| /*=============================================================================# |
| # Copyright (c) 2017, 2019 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.ltk.ui.templates; |
| |
| import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.ARRAY_CONTENTS; |
| import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.FIELD; |
| import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.PARAMETER; |
| import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.RETURN_TYPE; |
| import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.TYPE_ARGUMENT; |
| import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.TYPE_BOUND; |
| import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.IdentityHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.UUID; |
| |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.preference.PreferenceStore; |
| import org.eclipse.jface.text.templates.ContextTypeRegistry; |
| import org.eclipse.jface.text.templates.Template; |
| import org.eclipse.jface.text.templates.persistence.TemplateStore; |
| import org.eclipse.text.templates.TemplatePersistenceData; |
| import org.eclipse.ui.editors.text.templates.ContributionTemplateStore; |
| |
| import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet; |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.collections.ImList; |
| import org.eclipse.statet.jcommons.lang.NonNull; |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| |
| |
| /** |
| * Enhanced ContributionTemplateStore with |
| * <ul> |
| * <li>Ids for user templates</li> |
| * <li>Working copies</li> |
| * <li>Nullable annotations</li> |
| * <li>Notification</li> |
| * </ul> |
| */ |
| @NonNullByDefault({ PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT, ARRAY_CONTENTS }) |
| public class EnhTemplateStore extends ContributionTemplateStore { |
| |
| |
| public static final String USER_TEMPLATE_ID_PREFIX= "User."; //$NON-NLS-1$ |
| |
| |
| public static class EnhTemplatePersistenceData extends org.eclipse.jface.text.templates.persistence.TemplatePersistenceData { |
| |
| |
| private final Template orgTemplate; |
| private final boolean orgIsEnabled; |
| |
| |
| public EnhTemplatePersistenceData(final Template template, final boolean enabled, |
| final String id) { |
| super(template, enabled, id); |
| |
| this.orgTemplate= template; |
| this.orgIsEnabled= enabled; |
| } |
| |
| |
| @SuppressWarnings("null") |
| @Override |
| public String getId() { |
| return super.getId(); |
| } |
| |
| @Override |
| public boolean isUserAdded() { |
| return getId().startsWith(USER_TEMPLATE_ID_PREFIX); |
| } |
| |
| @Override |
| public boolean isCustom() { |
| return (isUserAdded() || super.isCustom()); |
| } |
| |
| |
| @Override |
| public void setTemplate(final Template template) { |
| super.setTemplate(template); |
| } |
| |
| @Override |
| public Template getTemplate() { |
| return super.getTemplate(); |
| } |
| |
| } |
| |
| |
| public class WorkingCopy extends TemplateStore { |
| |
| |
| private final Map<TemplatePersistenceData, EnhTemplatePersistenceData> copy2enh= new IdentityHashMap<>(); |
| |
| |
| private WorkingCopy() { |
| super(new PreferenceStore(), "templates"); //$NON-NLS-1$ |
| |
| loadFromEnh(true); |
| } |
| |
| |
| private void addExt(final EnhTemplatePersistenceData enhData, |
| final Map<EnhTemplatePersistenceData, TemplatePersistenceData> previous, |
| final boolean loadCustom) { |
| TemplatePersistenceData data= previous.get(enhData); |
| if (data != null) { |
| data.revert(); |
| } |
| else { |
| data= new EnhTemplatePersistenceData( |
| enhData.orgTemplate, |
| enhData.orgIsEnabled, |
| enhData.getId() ); |
| } |
| if (data.isUserAdded()) { |
| super.add(data); |
| } |
| else { |
| super.internalAdd(data); |
| } |
| if (loadCustom) { |
| data.setTemplate(enhData.getTemplate()); |
| data.setDeleted(enhData.isDeleted()); |
| data.setEnabled(enhData.isEnabled()); |
| } |
| else if (data.isUserAdded()) { |
| data.setDeleted(true); |
| } |
| |
| this.copy2enh.put(data, enhData); |
| } |
| |
| private void loadFromEnh(final boolean loadCustom) { |
| final Map<EnhTemplatePersistenceData, TemplatePersistenceData> previous= new IdentityHashMap<>(this.copy2enh.size()); |
| for (final TemplatePersistenceData data : this.copy2enh.keySet()) { |
| if (data instanceof EnhTemplatePersistenceData) { |
| previous.put(this.copy2enh.get(data), data); |
| } |
| } |
| |
| this.copy2enh.clear(); |
| try { |
| super.load(); // clear |
| } |
| catch (final IOException e) {} |
| |
| final ImList<EnhTemplatePersistenceData> enhDatas= getTemplateDatas(true); |
| for (final EnhTemplatePersistenceData enhData : enhDatas) { |
| addExt(enhData, previous, loadCustom); |
| } |
| } |
| |
| private void saveToEnh() { |
| final List<TemplatePersistenceData> internalDatas= internalGetTemplates(); |
| for (final TemplatePersistenceData data : internalDatas) { |
| EnhTemplatePersistenceData enhData= this.copy2enh.get(data); |
| if (!data.isDeleted()) { |
| if (enhData != null) { |
| enhData.setTemplate(data.getTemplate()); |
| enhData.setDeleted(false); |
| enhData.setEnabled(data.isEnabled()); |
| } |
| else { |
| enhData= doAdd(data); |
| if (enhData != null) { |
| this.copy2enh.put(data, enhData); |
| } |
| } |
| } |
| else { |
| // if (data.isUserAdded()) { |
| // this.ext2enh.remove(data); |
| // } |
| if (enhData != null) { |
| EnhTemplateStore.this.delete(enhData); |
| } |
| } |
| } |
| } |
| |
| |
| @Override |
| public void load() { |
| loadFromEnh(true); |
| } |
| |
| @Override |
| public void save() throws IOException { |
| saveToEnh(); |
| |
| EnhTemplateStore.this.save(); |
| } |
| |
| @Override |
| public void add(final TemplatePersistenceData data) { |
| super.add(data); |
| } |
| |
| @Override |
| public void delete(final TemplatePersistenceData data) { |
| data.setDeleted(true); |
| } |
| |
| @Override |
| public void restoreDeleted() { |
| super.restoreDeleted(); |
| } |
| |
| @Override |
| public void restoreDefaults(final boolean doSave) { |
| if (doSave) { |
| EnhTemplateStore.this.restoreDefaults(doSave); |
| |
| loadFromEnh(true); |
| } |
| else { |
| loadFromEnh(false); |
| } |
| } |
| |
| |
| @Override |
| protected void internalAdd(final org.eclipse.jface.text.templates.persistence.TemplatePersistenceData data) { |
| internalAdd((TemplatePersistenceData) data); |
| } |
| |
| @Override |
| public void add(final org.eclipse.jface.text.templates.persistence.TemplatePersistenceData data) { |
| add((TemplatePersistenceData) data); |
| } |
| |
| @Override |
| public void delete(final org.eclipse.jface.text.templates.persistence.TemplatePersistenceData data) { |
| delete((TemplatePersistenceData) data); |
| } |
| |
| } |
| |
| |
| private final ContextTypeRegistry registry; |
| |
| private final CopyOnWriteIdentityListSet<Runnable> listeners= new CopyOnWriteIdentityListSet<>(); |
| |
| |
| public EnhTemplateStore(final ContextTypeRegistry registry, |
| final IPreferenceStore store, final String key) { |
| super(registry, store, key); |
| |
| this.registry= registry; |
| } |
| |
| |
| public ContextTypeRegistry getContextTypeRegistry() { |
| return this.registry; |
| } |
| |
| |
| public void addListener(final Runnable listener) { |
| this.listeners.add(listener); |
| } |
| |
| public void removeListener(final Runnable listener) { |
| this.listeners.remove(listener); |
| } |
| |
| private void notifyListeners() { |
| for (final Runnable runnable : this.listeners.toList()) { |
| runnable.run(); |
| } |
| } |
| |
| |
| public ImList<EnhTemplatePersistenceData> getTemplateDatas(final boolean includeDeleted) { |
| return ImCollections.newList(getTemplateData(includeDeleted)); |
| } |
| |
| @Override |
| public EnhTemplatePersistenceData[] getTemplateData(final boolean includeDeleted) { |
| final List<TemplatePersistenceData> internalDatas= internalGetTemplates(); |
| final List<EnhTemplatePersistenceData> datasList= new ArrayList<>(internalDatas.size()); |
| for (final TemplatePersistenceData data : internalDatas) { |
| if (includeDeleted || !data.isDeleted()) { |
| datasList.add((EnhTemplatePersistenceData) data); |
| } |
| } |
| return datasList.toArray(new @NonNull EnhTemplatePersistenceData[datasList.size()]); |
| } |
| |
| @Override |
| public @Nullable EnhTemplatePersistenceData getTemplateData(final String id) { |
| nonNullAssert(id); |
| final List<TemplatePersistenceData> internalDatas= internalGetTemplates(); |
| for (final TemplatePersistenceData data : internalDatas) { |
| if (id.equals(data.getId())) { |
| return (EnhTemplatePersistenceData) data; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| protected void internalAdd(final TemplatePersistenceData data) { |
| if (!data.isCustom()) { |
| final EnhTemplatePersistenceData enhData; |
| if (data instanceof EnhTemplatePersistenceData) { |
| enhData= (EnhTemplatePersistenceData) data; |
| } |
| else { |
| enhData= new EnhTemplatePersistenceData(data.getTemplate(), |
| data.isEnabled(), nonNullAssert(data.getId()) ); |
| } |
| super.internalAdd(enhData); |
| } |
| } |
| |
| @Override |
| public void add(final TemplatePersistenceData data) { |
| doAdd(data); |
| } |
| |
| private @Nullable EnhTemplatePersistenceData doAdd(final TemplatePersistenceData data) { |
| String id= data.getId(); |
| // contributed |
| if (id != null && !id.startsWith(USER_TEMPLATE_ID_PREFIX) ) { |
| final EnhTemplatePersistenceData enhData= getTemplateData(id); |
| if (enhData != null) { |
| enhData.setTemplate(data.getTemplate()); |
| enhData.setDeleted(data.isDeleted()); |
| enhData.setEnabled(data.isEnabled()); |
| return enhData; |
| } |
| id= null; |
| } |
| // user added |
| { if (data.isDeleted()) { |
| return null; |
| } |
| if (id == null || getTemplateData(id) != null) { |
| do { |
| id= USER_TEMPLATE_ID_PREFIX + UUID.randomUUID(); |
| } while (getTemplateData(id) != null); |
| } |
| final EnhTemplatePersistenceData enhData= new EnhTemplatePersistenceData(data.getTemplate(), |
| data.isEnabled(), id ); |
| super.add(enhData); |
| return enhData; |
| } |
| } |
| |
| @Override |
| public void delete(final TemplatePersistenceData data) { |
| super.delete(data); |
| } |
| |
| |
| @Override |
| public void load() throws IOException { |
| super.load(); |
| } |
| |
| @Override |
| public void save() throws IOException { |
| super.save(); |
| |
| notifyListeners(); |
| } |
| |
| @Override |
| public void restoreDefaults(final boolean doSave) { |
| super.restoreDefaults(doSave); |
| |
| if (doSave) { |
| notifyListeners(); |
| } |
| } |
| |
| |
| @Override |
| public Template[] getTemplates() { |
| return super.getTemplates(); |
| } |
| |
| @Override |
| public Template[] getTemplates(final @Nullable String contextTypeId) { |
| return super.getTemplates(contextTypeId); |
| } |
| |
| @Override |
| public @Nullable Template findTemplateById(final String id) { |
| return super.findTemplateById(id); |
| } |
| |
| @Override |
| public @Nullable Template findTemplate(final String name, final @Nullable String contextTypeId) { |
| return super.findTemplate(name, contextTypeId); |
| } |
| |
| @Override |
| public @Nullable Template findTemplate(final String name) { |
| return super.findTemplate(name); |
| } |
| |
| |
| @Override |
| protected void internalAdd(final org.eclipse.jface.text.templates.persistence.TemplatePersistenceData data) { |
| internalAdd((TemplatePersistenceData) data); |
| } |
| |
| @Override |
| public void add(final org.eclipse.jface.text.templates.persistence.TemplatePersistenceData data) { |
| add((TemplatePersistenceData) data); |
| } |
| |
| @Override |
| public void delete(final org.eclipse.jface.text.templates.persistence.TemplatePersistenceData data) { |
| delete((TemplatePersistenceData) data); |
| } |
| |
| |
| public WorkingCopy getWorkingCopy() { |
| return new WorkingCopy(); |
| } |
| |
| } |