org.freedesktop.mime.DefaultGlobService.java Source code

Java tutorial

Introduction

Here is the source code for org.freedesktop.mime.DefaultGlobService.java

Source

/**
 * SSHTOOLS Limited licenses this file to you under the Apache
 * License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.freedesktop.mime;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;

import org.apache.commons.vfs2.FileObject;
import org.freedesktop.AbstractFreedesktopService;

public class DefaultGlobService extends AbstractFreedesktopService<GlobEntry> implements GlobService {

    private Map<FileObject, GlobBase> globBases = new TreeMap<FileObject, GlobBase>(new FileObjectComparator());

    @Override
    protected Collection<GlobEntry> scanBase(FileObject base) throws IOException {
        FileObject f = base.resolveFile("globs");
        GlobBase globBase = new GlobBase();
        globBases.put(base, globBase);
        InputStream fin = f.getContent().getInputStream();
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(fin));
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (!line.equals("") && !line.startsWith("#")) {
                    int idx = line.indexOf(':');
                    if (idx == -1) {
                        throw new IOException(f + " contains invalid data '" + line + "'.");
                    }
                    String mimeType = line.substring(0, idx);
                    String pattern = line.substring(idx + 1);

                    // A single mime type may have several patterns
                    GlobEntry entry = globBase.byType.get(mimeType);
                    if (entry == null) {
                        entry = new GlobEntry(mimeType);
                        globBase.byType.put(mimeType, entry);
                    }
                    entry.addPattern(pattern);

                    // Provide a quick lookup table for simple patterns and
                    // explicit names
                    if (isSimplePattern(pattern) || !isExpression(pattern)) {
                        ArrayList<GlobEntry> entries = globBase.byPattern.get(pattern);
                        if (entries == null) {
                            entries = new ArrayList<GlobEntry>();
                            globBase.byPattern.put(pattern, entries);
                        }
                        if (!entries.contains(entry)) {
                            entries.add(entry);
                        }
                    }
                }
            }
        } finally {
            fin.close();
        }
        return globBase.byType.values();
    }

    public void removeBase(FileObject base) {
        super.removeBase(base);
        globBases.remove(base);
    }

    public GlobEntry match(String text) throws MagicRequiredException {
        for (FileObject base : getBasesInReverse()) {
            GlobEntry entry = match(base, text);
            if (entry != null) {
                return entry;
            } else {
                entry = match(base, text.toLowerCase());
                if (entry != null) {
                    return entry;
                }
            }
        }
        return null;
    }

    GlobEntry match(FileObject base, String text) throws MagicRequiredException {
        // First lookup explicit pattern
        GlobBase globBase = globBases.get(base);
        if (globBase.byPattern.containsKey(text)) {
            ArrayList<GlobEntry> arrayList = globBase.byPattern.get(text);
            if (arrayList.size() > 1) {
                throw new MagicRequiredException("Explicit pattern matches more than one entry, magic required.",
                        arrayList);
            }
            return arrayList.get(0);
        }

        // Now try lookup based on the longest available filename extension
        int idx = text.indexOf('.');
        while (idx > -1) {
            String extension = text.substring(idx + 1);
            String key = "*." + extension;
            if (globBase.byPattern.containsKey(key)) {
                ArrayList<GlobEntry> arrayList = globBase.byPattern.get(key);
                if (arrayList.size() > 1) {
                    throw new MagicRequiredException(
                            "Extension pattern matches more than one entry, magic required.", arrayList);
                }
                return arrayList.get(0);
            }
            idx = text.indexOf('.', idx + 1);
        }

        // Still no match, look at all the entries using regexp. matching
        // against each one
        GlobEntry match = null;
        for (GlobEntry entry : getEntities(base)) {
            for (String pattern : entry.getPatterns()) {
                if (text.matches(globToRE(pattern))) {
                    if (match == null) {
                        match = entry;
                    } else {
                        throw new MagicRequiredException(
                                "Regular expression pattern matches more than one entry, magic required.", match,
                                entry);
                    }
                }
            }
        }
        return match;
    }

    /**
     * Converts a Unix-style glob to a regular expression.
     * <p>
     * ? becomes ., * becomes .*, {aa,bb} becomes (aa|bb).
     * 
     * @param glob
     *            The glob pattern
     */
    public static String globToRE(String glob) {
        // TODO this is GPL code - replace
        final Object NEG = new Object();
        final Object GROUP = new Object();
        Stack<Object> state = new Stack<Object>();

        StringBuffer buf = new StringBuffer();
        boolean backslash = false;

        for (int i = 0; i < glob.length(); i++) {
            char c = glob.charAt(i);
            if (backslash) {
                buf.append('\\');
                buf.append(c);
                backslash = false;
                continue;
            }

            switch (c) {
            case '\\':
                backslash = true;
                break;
            case '?':
                buf.append('.');
                break;
            case '.':
            case '+':
            case '(':
            case ')':
                buf.append('\\');
                buf.append(c);
                break;
            case '*':
                buf.append(".*");
                break;
            case '|':
                if (backslash)
                    buf.append("\\|");
                else
                    buf.append('|');
                break;
            case '{':
                buf.append('(');
                if (i + 1 != glob.length() && glob.charAt(i + 1) == '!') {
                    buf.append('?');
                    state.push(NEG);
                } else
                    state.push(GROUP);
                break;
            case ',':
                if (!state.isEmpty() && state.peek() == GROUP)
                    buf.append('|');
                else
                    buf.append(',');
                break;
            case '}':
                if (!state.isEmpty()) {
                    buf.append(")");
                    if (state.pop() == NEG)
                        buf.append(".*");
                } else
                    buf.append('}');
                break;
            default:
                buf.append(c);
            }
        }

        return buf.toString();
    }

    private boolean isSimplePattern(String pattern) {
        if (!pattern.startsWith("*.")) {
            return false;
        }
        return !isExpression(pattern.substring(2));
    }

    private boolean isExpression(String pattern) {
        char ch;
        boolean found = false;
        for (int i = pattern.length() - 1; i >= 0 && !found; i--) {
            ch = pattern.charAt(i);
            switch (ch) {
            case '*':
            case '?':
            case '[':
            case ']':
            case '`':
            case '\'':
                found = true;
                break;
            }
        }
        return found;
    }

    private Collection<GlobEntry> bySimplePattern(String pattern) {
        if (!isSimplePattern(pattern)) {
            throw new IllegalArgumentException("Only simple patterns are cached.");
        }
        for (FileObject base : getBasesInReverse()) {
            Collection<GlobEntry> entries = globBases.get(base).byPattern.get(pattern);
            if (entries != null) {
                return entries;
            }
        }
        return null;
    }

    public GlobEntry getByMimeType(String mimeType) {
        for (FileObject base : getBasesInReverse()) {
            GlobEntry entry = globBases.get(base).byType.get(mimeType);
            if (entry != null) {
                return entry;
            }
        }
        return null;
    }

    class GlobBase {
        Map<String, GlobEntry> byType = new HashMap<String, GlobEntry>();
        Map<String, ArrayList<GlobEntry>> byPattern = new HashMap<String, ArrayList<GlobEntry>>();
    }

}