Package org.webslinger.modules.edit

Source Code of org.webslinger.modules.edit.Editor$Request

package org.webslinger.modules.edit;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.vfs.FileContent;
import org.apache.commons.vfs.FileObject;

import org.webslinger.Webslinger;
import org.webslinger.WebslingerServletContext;
import org.webslinger.collections.CollectionUtil;
import org.webslinger.container.CommonsVfsFileInfo;
import org.webslinger.container.FileInfo;
import org.webslinger.io.Charsets;
import org.webslinger.io.IOUtil;
import static org.webslinger.servlet.MultipartFormUtil.parseRequestAsList;
import static org.webslinger.servlet.MultipartFormUtil.createFileItem;

public class Editor implements HttpSessionBindingListener, Serializable {
    private static final Logger logger = Logger.getLogger(Editor.class.getName());
    protected final WebslingerServletContext servletContext;
    protected final FileObject base;
    protected final String prefix;
    protected final HashMap contexts = new HashMap();

    public Editor(WebslingerServletContext servletContext, FileObject base, String prefix) {
        this.servletContext = servletContext;
        this.base = base;
        this.prefix = prefix;
    }

    public void valueBound(HttpSessionBindingEvent event) {
    }

    public void valueUnbound(HttpSessionBindingEvent event) {
        Iterator it = contexts.values().iterator();
        while (it.hasNext()) {
            Context context = (Context) it.next();
            context.clear();
        }
        contexts.clear();
    }

    public static Editor getEditor(Webslinger webslinger) throws IOException {
        return getEditor(webslinger, webslinger.getWebslingerContainer().getRoot(), "www/");
    }

    public static Editor getEditor(Webslinger webslinger, FileObject root, String prefix) throws IOException {
        return new Editor(webslinger.getWebslingerServletContext(), root, prefix);
    }

    public class Request {
        protected final List items;
        public final Context context;

        public Request(String path, List items) throws EditorException {
            this.items = items;
            Iterator it = items.iterator();
            while (it.hasNext()) {
                FileItem item = (FileItem) it.next();
                String name = item.getFieldName();
                if (!name.equals("path")) continue;
                try {
                    path = IOUtil.readString(item.getInputStream(), Charsets.UTF8);
                    item.delete();
                    it.remove();
                } catch (IOException e) {
                    throw new EditorException("Couldn't process parameter(path)", e);
                }
            }
            context = Editor.this.getContext(path);
        }

        public Request(String path, HttpServletRequest request) throws EditorException, IOException {
            this(path, parseRequestAsList(request));
        }

        public void action_deleteAttributes(List errors) throws EditorException {
            Iterator it = items.iterator();
            while (it.hasNext()) {
                FileItem item = (FileItem) it.next();
                String name = item.getFieldName();
                try {
                    if (name.startsWith("delete.")) {
                        context.deleteAttribute(name.substring(7));
                    } else if (name.equals("deleted")) {
                        context.deleteAttribute(IOUtil.readString(item.getInputStream(), Charsets.UTF8));
                    } else {
                        continue;
                    }
                    item.delete();
                } catch (EditorException e) {
                    errors.add(e);
                } catch (IOException e) {
                    errors.add(new EditorException("Couldn't process parameter(" + name + ")", e));
                }
            }
        }

        public void action_setAttributes(List errors) throws EditorException {
            Iterator it = items.iterator();
            String attrName = null, attrValue = null;
            boolean doAdd = false;
            while (it.hasNext()) {
                FileItem item = (FileItem) it.next();
                String name = item.getFieldName();
                try {
                    if (name.equals("add")) {
                        doAdd = true;
                    } else if (name.startsWith("attr.")) {
                        context.setAttribute(name.substring(5), IOUtil.readString(item.getInputStream(), Charsets.UTF8));
                    } else if (name.equals("attrName")) {
                        attrName = IOUtil.readString(item.getInputStream(), Charsets.UTF8);
                    } else if (name.equals("attrValue")) {
                        attrValue = IOUtil.readString(item.getInputStream(), Charsets.UTF8);
                    } else {
                        continue;
                    }
                    item.delete();
                } catch (EditorException e) {
                    errors.add(e);
                } catch (IOException e) {
                    errors.add(new EditorException("Couldn't process parameter(" + name + ")", e));
                }
            }
            if (doAdd) {
                if (attrName == null) {
                    errors.add(new EditorException("Couldn't add attribute, name is null"));
                } else if (attrValue == null) {
                    errors.add(new EditorException("Couldn't add attribute(" + attrName + "), value is null"));
                } else {
                    context.setAttribute(attrName, attrValue);
                }
            }
        }

        protected FileItem checkItem(FileItem oldItem, FileItem newItem) {
            if (oldItem != null) oldItem.delete();
            return newItem;
        }

        public boolean action_content(List errors) throws EditorException {
            Iterator it = items.iterator();
            FileItem uploadedFile = null;
            boolean doUpload = false;
            while (it.hasNext()) {
                FileItem item = (FileItem) it.next();
                String name = item.getFieldName();
                if (name.equals("content")) {
                    context.setContent(item);
                } else if (name.equals("file")) {
                    if (uploadedFile != null) uploadedFile.delete();
                    uploadedFile = item;
                } else if (name.equals("upload")) {
                    doUpload = true;
                } else {
                    continue;
                }
                it.remove();
            }
            if (doUpload) {
                context.setContent(uploadedFile);
                context.setAttribute("content-type", uploadedFile.getContentType());
                return true;
            }
            return false;
        }

        protected String checkCommand(String command, String newCommand) throws EditorException {
            if (command == null || command.equals(newCommand)) return newCommand;
            throw new EditorException("Tried to override command(" + command + ") with (" + newCommand + ")");
        }

        public void action_command(List errors, boolean autoSave) throws EditorException {
            Iterator it = items.iterator();
            String command = !autoSave ? null : "save";
            while (it.hasNext()) {
                FileItem item = (FileItem) it.next();
                String name = item.getFieldName();
                if (name.startsWith("cmd.")) {
                    command = checkCommand(command, name.substring(4));
                } else if (name.equals("delete")) {
                    command = checkCommand(command, "delete");
                } else if (name.equals("revert")) {
                    command = checkCommand(command, "revert");
                } else if (name.equals("save")) {
                    command = checkCommand(command, "save");
                } else {
                    continue;
                }
                item.delete();
                it.remove();
            }
            if ("delete".equals(command)) {
                context.delete();
            } else if ("revert".equals(command)) {
                context.revert();
            } else if ("save".equals(command)) {
                context.save();
            } else {
            }
        }
    }

    public Request parseRequest(String path, HttpServletRequest request) throws EditorException {
        try {
            return new Request(path, request);
        } catch (IOException e) {
            throw new EditorException("Couldn't parse request", e);
        }
    }

    public Request parseRequest(String path, List items) throws EditorException {
        return new Request(path, items);
    }

    public String processRequest(String path, HttpServletRequest request) throws EditorException {
        Request req = parseRequest(path, request);
        ArrayList errors = new ArrayList();
        processRequest(req, errors);
        return req.context.filePath;
    }

    public void processRequest(Request request, List errors) throws EditorException {
        request.action_setAttributes(errors);
        request.action_deleteAttributes(errors);
        boolean autoSave = request.action_content(errors);
        request.action_command(errors, autoSave);
    }

    protected Context getContext(String path) throws EditorException {
        synchronized (contexts) {
            Context context = (Context) contexts.get(path);
            if (context != null) return context;
            context = new Context(path);
            contexts.put(path, context);
            return context;
        }
    }

    public String getDefaultExtension() throws IOException {
        return (String) servletContext.getConfigItem("Edit.DefaultExtension");
    }

    public String getDefaultEditor() throws IOException {
        return (String) servletContext.getConfigItem("Edit.DefaultEditor");
    }

    protected FileInfo createFileInfo(String path) throws EditorException {
        try {
            return new CommonsVfsFileInfo(servletContext.getContainer(), base.resolveFile(path));
        } catch (IOException e) {
            throw new EditorException("Couldn't get file info(" + path + ")", e);
        }
    }

    protected String findExtensionByMimeType(String contentType) throws EditorException {
        try {
            return servletContext.getContainer().findExtensionByMimeType(contentType);
        } catch (IOException e) {
            throw new EditorException("Couldn't find extension for(" + contentType + ")", e);
        }
    }

    protected String[] getExtensionList() throws EditorException {
        try {
            return servletContext.getContainer().getExtensionList();
        } catch (IOException e) {
            throw new EditorException("Couldn't get extension list", e);
        }
    }

    protected boolean checkForMigration(FileObject ptr, Map hookContext) throws EditorException {
        try {
            logger.fine("checkForMigration(" + ptr + ")");
            String[] extensions = getExtensionList();
            if (extensions == null || extensions.length == 0) return true;
            if (ptr.exists() || ptr.getName().getPath().length() == 0) return false;
            FileObject parent = ptr.getParent();
            if (checkForMigration(parent, hookContext)) return true;
            String baseName = ptr.getName().getBaseName();
            FileObject targetDir = parent.resolveFile(baseName);
            runHook("Migrate", hookContext, "createFolder", targetDir);
            targetDir.createFolder();
            List movePairs = new ArrayList(extensions.length);
            for (int i = 0; i < extensions.length; i++) {
                String extension = extensions[i];
                FileObject src = parent.resolveFile(ptr.getName().getBaseName() + extension);
                logger.finer("src=" + src);
                if (!src.exists()) continue;
                FileObject dest = targetDir.resolveFile("index" + extension);
                movePairs.add(new FileObject[] {src, dest});
            }
            runHook("Migrate", hookContext, "preMove", movePairs);
            for (int i = 0; i < movePairs.size(); i++) {
                FileObject[] pair = (FileObject[]) movePairs.get(i);
                pair[0].moveTo(pair[1]);
            }
            runHook("Migrate", hookContext, "postMove", movePairs);
            return true;
        } catch (IOException e) {
            throw new EditorException("Couldn't migrate(" + ptr.getName().getPath() + ")", e);
        } catch (ServletException e) {
            throw new EditorException("Couldn't migrate(" + ptr.getName().getPath() + ")", e);
        }
    }

    protected Object runHook(String hook, Map context) throws IOException, ServletException {
        return runHook(hook, context, null, null);
    }

    protected Object runHook(String hook, Map context, String command, Object payload) throws IOException, ServletException {
        return servletContext.run(servletContext.resolve("/WEB-INF/Events/Hooks/Edit/" + hook), context, command, payload);
    }

    private static class SyncFutureTask extends FutureTask {
        public SyncFutureTask(Callable callable) {
            super(callable);
        }

        public SyncFutureTask(Runnable runnable, Object result) {
            super(runnable, result);
        }

        public Object get() throws ExecutionException, InterruptedException {
            run();
            return super.get();
        }

        public Object get(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException {
            run();
            return super.get(timeout, unit);
        }
    }

    protected static class ContentFetcher implements Callable {
        protected final Content content;

        protected ContentFetcher(Content content) {
            this.content = content;
        }

        public Object call() throws Exception {
            content.fetchContent();
            return content.content;
        }
    }

    private static abstract class Content {
        protected FileItem content;
        private long lastModifiedTime;
        private final FutureTask task;

        protected Content() {
            task = new SyncFutureTask(new ContentFetcher(this));
        }

        public void cancel() {
            task.cancel(true);
            if (content != null) content.delete();
        }

        protected abstract void fetchContent() throws Exception;

        protected void setContent(FileItem content, long lastModifiedTime) {
            this.content = content;
            this.lastModifiedTime = lastModifiedTime;
        }

        protected boolean hasContent() {
            return content != null;
        }

        protected FileItem getContent() throws EditorException {
            try {
                try {
                    return (FileItem) task.get();
                } catch (ExecutionException e) {
                    throw e.getCause();
                }
            } catch (RuntimeException e) {
                throw e;
            } catch (Error e) {
                throw e;
            } catch (Throwable e) {
                throw (EditorException) new EditorException(e.getMessage()).initCause(e);
            }
        }

        protected long getLastModifiedTime() throws EditorException {
            getContent();
            return lastModifiedTime;
        }
    }

    private static class ExistingContent extends Content {
        private final FileObject file;

        protected ExistingContent(FileObject file) {
            this.file = file;
        }

        protected void fetchContent() throws Exception {
            FileItem content = createFileItem("content", file.getContent().getContentInfo().getContentType(), false, null);
            IOUtil.copy(file.getContent().getInputStream(), true, content.getOutputStream(), true);
            long lastModifiedTime = file.getContent().getLastModifiedTime();
            setContent(content, lastModifiedTime);
        }
    }

    private static class UploadedContent extends Content {
        private final FileItem content;
        private long lastModifiedTime;

        protected UploadedContent(FileItem content) {
            this.content = content;
            this.lastModifiedTime = System.currentTimeMillis();
        }

        protected boolean hasContent() {
            return true;
        }

        protected void fetchContent() throws Exception {
            setContent(content, lastModifiedTime);
        }
    }

    public class Context implements Serializable {
        public final String filePath;
        protected final HashMap originalAttributes = new HashMap();
        protected final HashMap attributes = new HashMap();
        protected final HashSet deletedAttributes = new HashSet();
        protected boolean exists;
        protected boolean modified;
        protected Content content;
        protected final HashMap hookDatas = new HashMap();

        protected Context(String filePath) throws EditorException {
            this.filePath = filePath;
            load();
        }

        // Hooks for EditorContentContext
        FileItem getContent() throws EditorException {
            return content.getContent();
        }

        public Map getHookData(String hookName) {
            Map hookData = (Map) hookDatas.get(hookName);
            if (hookData == null) {
                hookData = new HashMap();
                hookDatas.put(hookName, hookData);
            }
            return hookData;
        }

        public void setAttribute(String name, Object value) {
            if (value == null) {
                deleteAttribute(name);
                return;
            }
            attributes.put(name, value);
            deletedAttributes.remove(name);
        }

        public void setAllAttributes(Map toSet) {
            Iterator it = toSet.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry entry = (Map.Entry) it.next();
                setAttribute((String) entry.getKey(), entry.getValue());
            }
        }

        public void deleteAttribute(String name) {
            attributes.remove(name);
            deletedAttributes.add(name);
        }

        public void deleteAllAttributes(Set toDelete) {
            deletedAttributes.addAll(toDelete);
        }

        public Set getDeletedAttributes() {
            return Collections.unmodifiableSet(deletedAttributes);
        }

        public Object getAttribute(String name) {
            if (deletedAttributes.contains(name)) return null;
            return attributes.get(name);
        }

        public Map getAttributes() {
            return Collections.unmodifiableMap(attributes);
        }

        public boolean hasContent() {
            return content.hasContent();
        }

        public boolean exists() {
            return exists;
        }

        public boolean isModified() {
            return modified;
        }

        public long getLastModifiedTime() throws EditorException {
            return content.getLastModifiedTime();
        }

        public String getString() throws EditorException {
            try {
                return content != null ? IOUtil.readString(content.getContent().getInputStream()) : null;
            } catch (EditorException e) {
                throw e;
            } catch (RuntimeException e) {
                throw e;
            } catch (Error e) {
                throw e;
            } catch (Throwable e) {
                throw new EditorException("Couldn't get string from content(" + filePath + ")", e);
            }
        }

        public String getContentType() throws EditorException {
            return (String) getAttribute("content-type");
        }

        public String getContentEncoding() throws EditorException {
            return (String) getAttribute("content-encoding");
        }

        protected FileObject getFile() throws IOException {
            return getFileInfo().getFile();
        }

        void clearContent() {
            if (content != null) {
                content.cancel();
                content = null;
            }
        }

        public void setContent(FileItem content) {
            modified = true;
            clearContent();
            this.content = new UploadedContent(content);
        }

        public void clear() {
            attributes.clear();
            clearContent();
            deletedAttributes.clear();
            originalAttributes.clear();
        }

        protected void load() throws EditorException {
            try {
                FileObject file = getFileInfo().getFile();
                clearContent();
                if (file.exists()) {
                    Iterator it = file.getContent().getAttributes().entrySet().iterator();
                    while (it.hasNext()) {
                        Map.Entry entry = (Map.Entry) it.next();
                        originalAttributes.put((String) entry.getKey(), entry.getValue());
                    }
                    attributes.putAll(originalAttributes);
                    this.content = new ExistingContent(file);
                    exists = true;
                }
            } catch (EditorException e) {
                throw e;
            } catch (IOException e) {
                throw new EditorException("Couldn't load content(" + filePath + ")", e);
            }
        }

        protected void checkForMigration(FileObject ptr) throws EditorException, IOException, ServletException {
            Editor.this.checkForMigration(ptr, CollectionUtil.toMap("fileInfo", getFileInfo(), "filePath", filePath, "fileContext", this));
        }

        protected FileInfo checkHook(String hook, String command) throws EditorException, IOException, ServletException {
            FileInfo fileInfo = getFileInfo();
            Object result = runHook(hook, CollectionUtil.toMap("fileInfo", fileInfo, "filePath", filePath, "fileContext", this));
            if (result instanceof Collection) {
                Iterator it = ((Collection) result).iterator();
                while (it.hasNext()) {
                    Object hookResult = it.next();
                    if (hookResult instanceof Boolean && Boolean.FALSE.equals(hookResult)) {
                        throw new EditorException("Couldn't " + command + "(" + filePath + "), hook disallowed");
                    }
                }
            }
            return fileInfo;
        }

        protected void hook(String hook, FileInfo fileInfo) throws IOException, ServletException {
            runHook(hook, CollectionUtil.toMap("fileInfo", fileInfo, "filePath", filePath, "fileContext", this));
        }

        public void delete() throws EditorException {
            try {
                FileInfo fileInfo = checkHook("PreDelete", "delete");
                fileInfo.getFile().delete();
                refresh(fileInfo);
                hook("PostDelete", fileInfo);
            } catch (EditorException e) {
                throw e;
            } catch (ServletException e) {
                throw new EditorException("Couldn't delete file(" + filePath + ")", e);
            } catch (IOException e) {
                throw new EditorException("Couldn't delete file(" + filePath + ")", e);
            }
            clear();
        }

        public void revert() throws EditorException {
            clear();
            load();
        }

        public void save() throws EditorException {
            hookDatas.clear();
            try {
                logger.info(this + ".save()");
                FileInfo fileInfo = checkHook("PreSave", "save");
                FileObject file = fileInfo.getFile();
                checkForMigration(file.getParent());
                FileContent fileContent = file.getContent();
                if (hasContent()) IOUtil.copy(getContent().getInputStream(), true, fileContent.getOutputStream(), true);
                Iterator it = deletedAttributes.iterator();
                while (it.hasNext()) {
                    String name = (String) it.next();
                    if (fileContent.hasAttribute(name)) {
                        fileContent.removeAttribute(name);
                    } else {
                        fileContent.setAttribute(name, null);
                    }
                }
                Map defaultAttributes = fileInfo.getDefaultAttributes();
                it = attributes.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry entry = (Map.Entry) it.next();
                    String name = (String) entry.getKey();
                    Object value = entry.getValue();
                    if (value.equals(defaultAttributes.get(name))) {
                        fileContent.removeAttribute(name);
                    } else {
                        fileContent.setAttribute(name, value);
                    }
                }
                refresh(fileInfo);
                hook("PostSave", fileInfo);
                clear();
                load();
            } catch (EditorException e) {
                throw e;
            } catch (ServletException e) {
                throw new EditorException("Couldn't save(" + filePath + ")", e);
            } catch (IOException e) {
                throw new EditorException("Couldn't save(" + filePath + ")", e);
            } finally {
                hookDatas.clear();
            }
        }

        protected void refresh(FileInfo fileInfo) throws IOException {
            FileObject file = servletContext.getFile(filePath);
            servletContext.getContainer().getFileInfo(file).refresh();
            fileInfo.refresh();
        }

        protected FileInfo getFileInfo() throws IOException {
            try {
                FileInfo fileInfo = Editor.this.createFileInfo(prefix + filePath);
                FileObject file = fileInfo.getFile();
                String contentType = (String) attributes.get("content-type");
                if (file.getType().hasChildren()) {
                    String extension;
                    if (contentType != null) {
                        extension = Editor.this.findExtensionByMimeType(contentType);
                        if (extension == null) extension = Editor.this.getDefaultExtension();
                    } else {
                        extension = Editor.this.getDefaultExtension();
                    }
                    String name = "index";
                    String defaultIndex = (String) file.getContent().getAttribute("default-index");
                    if (defaultIndex != null) name = defaultIndex;
                    FileObject base = file;
                    file = base.resolveFile(name);
                    if (file.exists()) {
                        fileInfo = Editor.this.createFileInfo(file.getName().getPath());
                    } else {
                        if (extension != null && extension.length() > 0) {
                            file = base.resolveFile(name + '.' + extension);
                            fileInfo = Editor.this.createFileInfo(file.getName().getPath());
                        }
                    }
                } else {
                if (file.getName().getExtension().length() == 0) {
                    // no extension
                    String extension;
                    if (contentType == null && !servletContext.getContainer().getDefaultMimeType().equals(fileInfo.getContentType())) {
                        contentType = (String) fileInfo.getAttribute("content-type");
                    }
                    if (contentType != null) {
                        extension = Editor.this.findExtensionByMimeType(contentType);
                        if (extension == null) extension = Editor.this.getDefaultExtension();
                    } else {
                        extension = Editor.this.getDefaultExtension();
                    }
                    if (extension != null && extension.length() > 0) {
                        file = file.getParent().resolveFile(file.getName().getBaseName() + '.' + extension);
                        fileInfo = Editor.this.createFileInfo(file.getName().getPath());
                    }
                }
                }
                return fileInfo;
            } catch (EditorException e) {
                throw e;
            } catch (IOException e) {
                throw new EditorException("Couldn't find file info(" + filePath + ")", e);
            }
        }

        public String getType() throws EditorException {
            try {
                String type = (String) attributes.get("editor-type");
                if (type != null) return type;
                FileInfo fileInfo = getFileInfo();
                type = (String) fileInfo.getAttribute("editor-type");
                if (type != null) return type;
                return Editor.this.getDefaultEditor();
            } catch (EditorException e) {
                throw e;
            } catch (IOException e) {
                throw new EditorException("Couldn't find editor type(" + filePath + ")", e);
            }
        }

        public String toString() {
            return "EditorContext(" + filePath + ")";
        }
    }
}
TOP

Related Classes of org.webslinger.modules.edit.Editor$Request

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.