/** * */ package org.goko.tools.macro.service; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.commons.collections.CollectionUtils; import org.eclipse.core.runtime.NullProgressMonitor; import org.goko.core.common.exception.GkException; import org.goko.core.common.exception.GkTechnicalException; import org.goko.core.common.utils.CacheByCode; import org.goko.core.common.utils.CacheById; import org.goko.core.common.utils.CacheByKey; import org.goko.core.common.utils.SequentialIdGenerator; import org.goko.core.common.utils.UniqueCacheByCode; import org.goko.core.gcode.element.IGCodeProvider; import org.goko.core.gcode.service.GCodeProviderDeleteEvent; import org.goko.core.gcode.service.IGCodeProviderDeleteVetoableListener; import org.goko.core.gcode.service.IGCodeProviderRepositoryListener; import org.goko.core.gcode.service.IGCodeService; import org.goko.tools.macro.bean.GCodeMacro; import org.goko.tools.macro.bean.GCodeMacroReferenceById; /** * Default GCodeMacro services. Stores macro in a folder inside the workspace * @author Psyko * @date 15 oct. 2016 */ public class DefaultGCodeMacroService implements IGCodeMacroService { /** Service ID */ private static final String SERVICE_ID ="org.goko.tools.macro.servicee.DefaultGCodeMacroService"; /** The cache of providers */ private CacheById<IGCodeProvider> cacheProviders; /** The cache of macros */ private CacheById<GCodeMacro> cacheMacros; /** The cache of macros */ private CacheByCode<GCodeMacro> cacheMacrosByCode; /** The cache of provider by macro */ private CacheByKey<Integer, Integer> cacheProviderByMacro; /** The GCode service used to transform the macros */ private IGCodeService gcodeService; /** The list of modifier listener */ private List<IGCodeMacroServiceListener> listenerList; /** The list of repository listener */ private List<IGCodeProviderRepositoryListener> gcodeListenerList; /** The list of modifier listener */ private List<IGCodeProviderDeleteVetoableListener> gcodeProviderDeleteListenerList; /** * */ public DefaultGCodeMacroService() { this.listenerList = new CopyOnWriteArrayList<IGCodeMacroServiceListener>(); this.gcodeListenerList = new CopyOnWriteArrayList<IGCodeProviderRepositoryListener>(); this.gcodeProviderDeleteListenerList = new CopyOnWriteArrayList<IGCodeProviderDeleteVetoableListener>(); } /** (inheritDoc) * @see org.goko.core.common.service.IGokoService#getServiceId() */ @Override public String getServiceId() throws GkException { return SERVICE_ID; } /** (inheritDoc) * @see org.goko.core.common.service.IGokoService#start() */ @Override public void start() throws GkException { this.cacheProviders = new CacheById<IGCodeProvider>(new SequentialIdGenerator()); this.cacheMacros = new CacheById<GCodeMacro>(new SequentialIdGenerator()); this.cacheMacrosByCode = new UniqueCacheByCode<GCodeMacro>(); this.cacheProviderByMacro = new CacheByKey<Integer, Integer>(); } protected void initFromMacroFolder(){ } /** (inheritDoc) * @see org.goko.core.common.service.IGokoService#stop() */ @Override public void stop() throws GkException { } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#addGCodeMacro(org.goko.tools.macro.bean.GCodeMacro) */ @Override public void addGCodeMacro(GCodeMacro gcodeMacro) throws GkException { this.cacheMacros.add(gcodeMacro); this.cacheMacrosByCode.add(gcodeMacro); notifyGCodeMacroCreate(gcodeMacro); } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#getGCodeMacro(java.lang.Integer) */ @Override public GCodeMacro getGCodeMacro(Integer id) throws GkException { return cacheMacros.get(id); } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#getGCodeMacro(java.lang.String) */ @Override public GCodeMacro getGCodeMacro(String code) throws GkException { return cacheMacrosByCode.get(code); } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#findGCodeMacro(java.lang.String) */ @Override public GCodeMacro findGCodeMacro(String code) throws GkException { return cacheMacrosByCode.find(code); } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#findGCodeMacro(java.lang.Integer) */ @Override public GCodeMacro findGCodeMacro(Integer id) throws GkException { return cacheMacros.find(id); } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#getGCodeMacro() */ @Override public List<GCodeMacro> getGCodeMacro() throws GkException { return cacheMacros.get(); } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#updateGCodeMacro(org.goko.tools.macro.bean.GCodeMacro) */ @Override public void updateGCodeMacro(GCodeMacro macro) throws GkException { cacheMacros.remove(macro.getId()); cacheMacros.add(macro); // Force update the GCode provider updateGCodeProvider(macro); IGCodeProvider provider = getGCodeProviderByMacro(macro.getId()); notifyGCodeMacroUpdate(macro); notifyGCodeProviderUpdate(provider); } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#deleteGCodeMacro(org.goko.tools.macro.bean.GCodeMacro) */ @Override public void deleteGCodeMacro(GCodeMacro macro) throws GkException { IGCodeProvider provider = getGCodeProviderByMacro(macro.getId()); if(provider.isLocked()){ throw new GkTechnicalException("Cannot delete the selected macro as it is currently in use."); } if(checkDeleteGCodeProvider(provider.getId())){ notifyBeforeGCodeMacroDelete(macro); notifyBeforeGCodeProviderDelete(provider); cacheMacros.remove(macro); cacheMacrosByCode.remove(macro); notifyAfterGCodeProviderDelete(provider); notifyAfterGCodeMacroDelete(macro); } } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#deleteGCodeMacro(java.lang.String) */ @Override public void deleteGCodeMacro(String code) throws GkException { GCodeMacro macro = getGCodeMacro(code); deleteGCodeMacro(macro); } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#deleteGCodeMacro(java.lang.Integer) */ @Override public void deleteGCodeMacro(Integer id) throws GkException { GCodeMacro macro = getGCodeMacro(id); deleteGCodeMacro(macro); } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#getGCodeProvider() */ @Override public List<IGCodeProvider> getGCodeProvider() throws GkException { return cacheProviders.get(); } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#getGCodeProviderByMacro(java.lang.Integer) */ @Override public IGCodeProvider getGCodeProviderByMacro(Integer idMacro) throws GkException { GCodeMacro macro = getGCodeMacro(idMacro); if(!cacheProviderByMacro.exist(idMacro)){ updateGCodeProvider(macro); } return getGCodeProvider(cacheProviderByMacro.get(idMacro)); } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#internalGetGCodeProviderByMacro(java.lang.Integer) */ @Override public IGCodeProvider internalGetGCodeProviderByMacro(Integer idMacro) throws GkException { GCodeMacro macro = getGCodeMacro(idMacro); if(!cacheProviderByMacro.exist(idMacro)){ updateGCodeProvider(macro); } return cacheProviders.get(cacheProviderByMacro.get(idMacro)); } protected void updateGCodeProvider(GCodeMacro macro) throws GkException{ IGCodeProvider provider = gcodeService.parse(macro.getContent(), new NullProgressMonitor()); provider.setCode(macro.getCode()); if(cacheProviderByMacro.exist(macro.getId())){ Integer previousProviderId = cacheProviderByMacro.get(macro.getId()); provider.setId(previousProviderId); cacheProviderByMacro.remove(macro.getId()); cacheProviders.remove(previousProviderId); } cacheProviders.add(provider); cacheProviderByMacro.add(macro.getId(), provider.getId()); notifyGCodeProviderUpdate(getGCodeProvider(provider.getId())); } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#getGCodeProvider(java.lang.Integer) */ @Override public IGCodeProvider getGCodeProvider(Integer id) throws GkException { return new GCodeMacroReferenceById(this, id); } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#internalGetGCodeProvider(java.lang.Integer) */ @Override public IGCodeProvider internalGetGCodeProvider(Integer id) throws GkException { return cacheProviders.get(id); } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#findGCodeProvider(java.lang.Integer) */ @Override public IGCodeProvider findGCodeProvider(Integer id) throws GkException { return cacheProviders.find(id); } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#getGCodeProvider(java.lang.String) */ @Override public IGCodeProvider getGCodeProvider(String code) throws GkException { return null; } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#lockGCodeProvider(java.lang.Integer) */ @Override public void lockGCodeProvider(Integer idGcodeProvider) throws GkException { // Lock state not handled in macro } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#unlockGCodeProvider(java.lang.Integer) */ @Override public void unlockGCodeProvider(Integer idGcodeProvider) throws GkException { // Lock state not handled in macro } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#addGCodeProvider(org.goko.core.gcode.element.IGCodeProvider) */ @Override public void addGCodeProvider(IGCodeProvider provider) throws GkException { cacheProviders.add(provider); } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#deleteGCodeProvider(java.lang.Integer) */ @Override public void deleteGCodeProvider(Integer id) throws GkException { GCodeMacro macro = getGCodeMacro(id); deleteGCodeMacro(macro); } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#addListener(org.goko.core.gcode.service.IGCodeProviderRepositoryListener) */ @Override public void addListener(IGCodeProviderRepositoryListener listener) throws GkException { gcodeListenerList.add(listener); } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#removeListener(org.goko.core.gcode.service.IGCodeProviderRepositoryListener) */ @Override public void removeListener(IGCodeProviderRepositoryListener listener) throws GkException { gcodeListenerList.remove(listener); } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#clearAll() */ @Override public void clearAll() throws GkException { cacheProviders.removeAll(); cacheMacros.removeAll(); cacheMacrosByCode.removeAll(); } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#addDeleteVetoableListener(org.goko.core.gcode.service.IGCodeProviderDeleteVetoableListener) */ @Override public void addDeleteVetoableListener(IGCodeProviderDeleteVetoableListener listener) throws GkException { if(!gcodeProviderDeleteListenerList.contains(listener)){ gcodeProviderDeleteListenerList.add(listener); } } /** (inheritDoc) * @see org.goko.core.gcode.service.IGCodeProviderRepository#removeDeleteVetoableListener(org.goko.core.gcode.service.IGCodeProviderDeleteVetoableListener) */ @Override public void removeDeleteVetoableListener(IGCodeProviderDeleteVetoableListener listener) throws GkException { if(gcodeProviderDeleteListenerList.contains(listener)){ gcodeProviderDeleteListenerList.remove(listener); } } /** * @param gcodeService the gcodeService to set */ public void setGCodeService(IGCodeService gcodeService) { this.gcodeService = gcodeService; } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#addListener(org.goko.tools.macro.service.IGCodeMacroServiceListener) */ @Override public void addListener(IGCodeMacroServiceListener listener) throws GkException { listenerList.add(listener); } /** (inheritDoc) * @see org.goko.tools.macro.service.IGCodeMacroService#removeListener(org.goko.tools.macro.service.IGCodeMacroServiceListener) */ @Override public void removeListener(IGCodeMacroServiceListener listener) throws GkException { listenerList.remove(listener); } /** * Notify the listener that the given GCodeMacro was created * @param macro the target GCodeMacro * @throws GkException GkException */ protected void notifyGCodeMacroCreate(GCodeMacro macro) throws GkException { if (CollectionUtils.isNotEmpty(listenerList)) { for (IGCodeMacroServiceListener listener : listenerList) { listener.onGCodeMacroCreate(macro); } } } /** * Notify the listener that the given GCodeMacro was updated * @param macro the target GCodeMacro * @throws GkException GkException */ protected void notifyGCodeMacroUpdate(GCodeMacro macro) throws GkException { if (CollectionUtils.isNotEmpty(listenerList)) { for (IGCodeMacroServiceListener listener : listenerList) { listener.onGCodeMacroUpdate(macro); } } } /** * Notify the listener that the given GCodeMacro is about to be deleted * @param macro the target GCodeMacro * @throws GkException GkException */ protected void notifyBeforeGCodeMacroDelete(GCodeMacro macro) throws GkException { if (CollectionUtils.isNotEmpty(listenerList)) { for (IGCodeMacroServiceListener listener : listenerList) { listener.beforeGCodeMacroDelete(macro); } } } /** * Notify the listener that the given GCodeMacro was deleted * @param macro the target GCodeMacro * @throws GkException GkException */ protected void notifyAfterGCodeMacroDelete(GCodeMacro macro) throws GkException { if (CollectionUtils.isNotEmpty(listenerList)) { for (IGCodeMacroServiceListener listener : listenerList) { listener.afterGCodeMacroDelete(macro); } } } /** * Notify the listener that the given GCodeProvider was updated * @param provider the target provider * @throws GkException GkException */ protected void notifyGCodeProviderUpdate(IGCodeProvider provider) throws GkException { if (CollectionUtils.isNotEmpty(listenerList)) { for (IGCodeProviderRepositoryListener listener : gcodeListenerList) { listener.onGCodeProviderUpdate(provider); } } } /** * Notify the listener that the given GCodeProvider is about to be deleted * @param provider the target provider * @throws GkException GkException */ protected void notifyBeforeGCodeProviderDelete(IGCodeProvider provider) throws GkException { if (CollectionUtils.isNotEmpty(listenerList)) { for (IGCodeProviderRepositoryListener listener : gcodeListenerList) { listener.beforeGCodeProviderDelete(provider); } } } /** * Notify the listener that the given GCodeProvider was deleted * @param provider the target provider * @throws GkException GkException */ protected void notifyAfterGCodeProviderDelete(IGCodeProvider provider) throws GkException { if (CollectionUtils.isNotEmpty(listenerList)) { for (IGCodeProviderRepositoryListener listener : gcodeListenerList) { listener.afterGCodeProviderDelete(provider); } } } /** * Calls every delete listener to make sure the GCodeProvider can be deleted * @param idGCodeProvider the id of the provider to delete * @return <code>true</code> if it can be deleted, <code>false</code> otherwise */ protected boolean checkDeleteGCodeProvider(Integer idGCodeProvider){ GCodeProviderDeleteEvent event = new GCodeProviderDeleteEvent(idGCodeProvider); for (IGCodeProviderDeleteVetoableListener deleteListener : gcodeProviderDeleteListenerList) { deleteListener.beforeDelete(event); if(!event.isDoIt()){ break; } } return event.isDoIt(); } }