001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.vfs2.provider.local;
018
019import org.apache.commons.vfs2.FileName;
020import org.apache.commons.vfs2.FileSystemException;
021import org.apache.commons.vfs2.FileType;
022
023/**
024 * A parser for Windows file names.
025 */
026public class WindowsFileNameParser extends LocalFileNameParser {
027    /**
028     * Pops the root prefix off a URI, which has had the scheme removed.
029     */
030    @Override
031    protected String extractRootPrefix(final String uri, final StringBuilder name) throws FileSystemException {
032        return extractWindowsRootPrefix(uri, name);
033    }
034
035    @Override
036    protected FileName createFileName(final String scheme, final String rootFile, final String path,
037            final FileType type) {
038        return new WindowsFileName(scheme, rootFile, path, type);
039    }
040
041    /**
042     * Extracts a Windows root prefix from a name.
043     */
044    private String extractWindowsRootPrefix(final String uri, final StringBuilder name) throws FileSystemException {
045        // Looking for:
046        // ('/'){0, 3} <letter> ':' '/'
047        // ['/'] '//' <name> '/' <name> ( '/' | <end> )
048
049        // Skip over first 4 (unc) leading '/' chars
050        int startPos = 0;
051        final int maxlen = Math.min(4, name.length());
052        for (; startPos < maxlen && name.charAt(startPos) == '/'; startPos++) {
053        }
054        if (startPos == maxlen && name.length() > (startPos + 1) && name.charAt(startPos + 1) == '/') {
055            // Too many '/'
056            throw new FileSystemException("vfs.provider.local/not-absolute-file-name.error", uri);
057        }
058        name.delete(0, startPos);
059
060        // Look for drive name
061        final String driveName = extractDrivePrefix(name);
062        if (driveName != null) {
063            return driveName;
064        }
065
066        // Look for UNC name
067        if (startPos < 2) {
068            throw new FileSystemException("vfs.provider.local/not-absolute-file-name.error", uri);
069        }
070
071        return "//" + extractUNCPrefix(uri, name);
072    }
073
074    /**
075     * Extracts a drive prefix from a path. Leading '/' chars have been removed.
076     */
077    private String extractDrivePrefix(final StringBuilder name) {
078        // Looking for <letter> ':' '/'
079        if (name.length() < 3) {
080            // Too short
081            return null;
082        }
083        final char ch = name.charAt(0);
084        if (ch == '/' || ch == ':') {
085            // Missing drive letter
086            return null;
087        }
088        if (name.charAt(1) != ':') {
089            // Missing ':'
090            return null;
091        }
092        if (name.charAt(2) != '/') {
093            // Missing separator
094            return null;
095        }
096
097        final String prefix = name.substring(0, 2);
098        name.delete(0, 2);
099
100        return prefix.intern();
101    }
102
103    /**
104     * Extracts a UNC name from a path. Leading '/' chars have been removed.
105     */
106    private String extractUNCPrefix(final String uri, final StringBuilder name) throws FileSystemException {
107        // Looking for <name> '/' <name> ( '/' | <end> )
108
109        // Look for first separator
110        final int maxpos = name.length();
111        int pos = 0;
112        for (; pos < maxpos && name.charAt(pos) != '/'; pos++) {
113        }
114        pos++;
115        if (pos >= maxpos) {
116            throw new FileSystemException("vfs.provider.local/missing-share-name.error", uri);
117        }
118
119        // Now have <name> '/'
120        final int startShareName = pos;
121        for (; pos < maxpos && name.charAt(pos) != '/'; pos++) {
122        }
123        if (pos == startShareName) {
124            throw new FileSystemException("vfs.provider.local/missing-share-name.error", uri);
125        }
126
127        // Now have <name> '/' <name> ( '/' | <end> )
128        final String prefix = name.substring(0, pos);
129        name.delete(0, pos);
130        return prefix;
131    }
132}