/*
documentr - Edit, maintain, and present software documentation on the web.
Copyright (C) 2012-2013 Maik Schreiber
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.blizzy.documentr.markdown.macro;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.Lifecycle;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import de.blizzy.documentr.data.IDataHandler;
@Component
@Slf4j
public class MacroFactory implements Lifecycle {
@Autowired
private ListableBeanFactory beanFactory;
@Autowired
private GroovyMacroScanner groovyMacroScanner;
private Map<String, IMacro> macros = Maps.newHashMap();
private AtomicBoolean running = new AtomicBoolean();
// cannot use @PostConstruct here because it is possible that not all beans in the context
// have already been constructed, thus causing us to not pick up some macros
@Override
public void start() {
rescanAllMacros();
running.set(true);
}
@Override
public void stop() {
running.set(false);
}
@Override
public boolean isRunning() {
return running.get();
}
private void waitForRunning() {
while (!running.get()) {
log.trace("wait for running"); //$NON-NLS-1$
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// ignore
}
}
}
private void rescanAllMacros() {
macros.clear();
registerMacrosFromClasses();
registerGroovyMacros();
}
private void registerMacrosFromClasses() {
log.info("registering macros from classes"); //$NON-NLS-1$
Collection<IMacro> macros = beanFactory.getBeansOfType(IMacro.class).values();
for (IMacro macro : macros) {
registerMacro(macro);
}
}
private void registerGroovyMacros() {
Set<IMacro> macros = groovyMacroScanner.findGroovyMacros();
for (IMacro macro : macros) {
registerMacro(macro);
}
}
private void registerMacro(IMacro macro) {
IMacroDescriptor descriptor = macro.getDescriptor();
String macroName = descriptor.getMacroName();
log.debug("registering macro: {}", macroName); //$NON-NLS-1$
if (!macros.containsKey(macroName)) {
macros.put(macroName, macro);
} else {
log.warn("duplicate macro: {}", macroName); //$NON-NLS-1$
}
}
public IMacro get(String macroName) {
waitForRunning();
return macros.get(macroName);
}
public Set<IMacroDescriptor> getDescriptors() {
waitForRunning();
Function<IMacro, IMacroDescriptor> function = new Function<IMacro, IMacroDescriptor>() {
@Override
public IMacroDescriptor apply(IMacro macro) {
return macro.getDescriptor();
}
};
return Sets.newHashSet(Collections2.transform(macros.values(), function));
}
public List<String> listGroovyMacros() {
return groovyMacroScanner.listMacros();
}
public String getGroovyMacroCode(String name) throws IOException {
return groovyMacroScanner.getMacroCode(name);
}
public List<CompilationMessage> verifyGroovyMacro(String code) {
return groovyMacroScanner.verifyMacro(code);
}
public void saveGroovyMacro(String name, String code) throws IOException {
groovyMacroScanner.saveMacro(name, code);
rescanAllMacros();
}
public void deleteGroovyMacro(String name) throws IOException {
groovyMacroScanner.deleteMacro(name);
rescanAllMacros();
}
public Object getData(String macroName, String request, Map<String, String[]> parameterMap, Authentication authentication) {
IMacro macro = get(macroName);
if (macro == null) {
throw new IllegalArgumentException("unknown macro: " + macroName); //$NON-NLS-1$
}
Class<? extends IDataHandler> dataHandlerClass = macro.getDescriptor().getDataHandlerClass();
if (dataHandlerClass == null) {
throw new IllegalArgumentException("macro does not have an associated data handler: " + macroName); //$NON-NLS-1$
}
try {
IDataHandler dataHandler = dataHandlerClass.newInstance();
dataHandler.setBeanFactory(beanFactory);
return dataHandler.getData(request, parameterMap, authentication);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
throw new RuntimeException(cause);
}
}
}