001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 * 
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id: IdUtils.java 19046 2007-05-21 13:03:50Z sfermigier $
020 */
021
022package org.nuxeo.common.utils;
023
024import java.util.ArrayList;
025import java.util.Date;
026import java.util.List;
027import java.util.Random;
028import java.util.regex.Pattern;
029
030/**
031 * Utils for identifier generation.
032 *
033 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
034 */
035public final class IdUtils {
036
037    private static final String WORD_SPLITTING_REGEXP = "[^a-zA-Z0-9]+";
038
039    public static final String UUID_TYPE_4_REGEXP = "[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}";
040
041    // TODO AT: dummy random, does not ensure uniqueness
042    private static final Random RANDOM = new Random(new Date().getTime());
043
044    // This is an utility class.
045    private IdUtils() {
046    }
047
048    /**
049     * Generates an unique string identifier.
050     */
051    public static String generateStringId() {
052        return String.valueOf(generateLongId());
053    }
054
055    /**
056     * Generates an unique long identifier.
057     */
058    public static long generateLongId() {
059        long r = RANDOM.nextLong();
060        if (r < 0) {
061            r = -r;
062        }
063        return r;
064    }
065
066    /**
067     * Generates an id from a non-null String.
068     * <p>
069     * Replaces accented characters from a string by their ascii equivalent, removes non alphanumerical characters and
070     * replaces spaces by the given wordSeparator character.
071     *
072     * @param s the original String
073     * @param wordSeparator the word separator to use (usually '-')
074     * @param lower if lower is true, remove upper case
075     * @param maxChars maximum longer of identifier characters
076     * @return the identifier String
077     */
078    public static String generateId(String s, String wordSeparator, boolean lower, int maxChars) {
079        s = StringUtils.toAscii(s);
080        s = s.trim();
081        if (lower) {
082            s = s.toLowerCase();
083        }
084        String[] words = s.split(WORD_SPLITTING_REGEXP);
085        // remove blank chars from words, did not get why they're not filtered
086        List<String> wordsList = new ArrayList<String>();
087        for (String word : words) {
088            if (word != null && word.length() > 0) {
089                wordsList.add(word);
090            }
091        }
092        if (wordsList.isEmpty()) {
093            return generateStringId();
094        }
095        StringBuilder sb = new StringBuilder();
096        String id;
097        if (maxChars > 0) {
098            // be sure at least one word is used
099            sb.append(wordsList.get(0));
100            for (int i = 1; i < wordsList.size(); i++) {
101                String newWord = wordsList.get(i);
102                if (sb.length() + newWord.length() > maxChars) {
103                    break;
104                } else {
105                    sb.append(wordSeparator).append(newWord);
106                }
107            }
108            id = sb.toString();
109            id = id.substring(0, Math.min(id.length(), maxChars));
110        } else {
111            id = StringUtils.join(wordsList.toArray(), wordSeparator);
112        }
113
114        return id;
115    }
116
117    /**
118     * Generates an id from a non-null String.
119     * <p>
120     * Uses default values for wordSeparator: '-', lower: true, maxChars: 24.
121     *
122     * @deprecated use {@link #generatePathSegment} instead, or {@link #generateId(String, String, boolean, int)}
123     *             depending on the use cases
124     */
125    @Deprecated
126    public static String generateId(String s) {
127        return generateId(s, "-", true, 24);
128    }
129
130    public static final Pattern STUPID_REGEXP = Pattern.compile("^[- .,;?!:/\\\\'\"]*$");
131
132    /**
133     * Generates a Nuxeo path segment from a non-null String.
134     * <p>
135     * Basically all characters are kept, except for slashes and initial/trailing spaces.
136     *
137     * @deprecated use {@link PathSegmentService} instead
138     */
139    @Deprecated
140    public static String generatePathSegment(String s) {
141        s = s.trim();
142        if (STUPID_REGEXP.matcher(s).matches()) {
143            return generateStringId();
144        }
145        return s.replace("/", "-");
146    }
147
148    /**
149     * Check if a given string has the pattern for UUID type 4
150     * 
151     * @since 5.7
152     */
153    public static boolean isValidUUID(String uuid) {
154        if (Pattern.compile(UUID_TYPE_4_REGEXP).matcher(uuid).matches()) {
155            return true;
156        }
157        return false;
158    }
159
160}