| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF 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.netbeans.modules.properties; |
| |
| import java.io.IOException; |
| import java.io.ObjectInput; |
| import java.io.ObjectOutput; |
| import java.lang.ref.Reference; |
| import java.lang.ref.SoftReference; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Locale; |
| import java.util.Set; |
| import org.openide.filesystems.FileObject; |
| import org.openide.loaders.ExtensionList; |
| import org.openide.loaders.MultiDataObject; |
| import org.openide.loaders.MultiFileLoader; |
| import org.openide.util.NbBundle; |
| import org.openide.util.io.SafeException; |
| |
| /** |
| * Data loader which recognizes properties files. |
| * This class is final only for performance reasons, |
| * can be unfinaled if desired. |
| * |
| * @author Ian Formanek, Petr Jiricka |
| * @author Marian Petras |
| */ |
| public final class PropertiesDataLoader extends MultiFileLoader { |
| |
| /** Extension for properties files. */ |
| static final String PROPERTIES_EXTENSION = "properties"; // NOI18N |
| |
| /** Properties MIME type*/ |
| static final String PROPERTIES_MIME_TYPE = "text/x-properties"; //NOI18N |
| |
| /** Character used to separate parts of bundle properties file name */ |
| public static final char PRB_SEPARATOR_CHAR = '_'; |
| |
| /** Generated serial version UID. */ |
| static final long serialVersionUID =4384899552891479449L; |
| |
| /** name of property with extensions */ |
| public static final String PROP_EXTENSIONS = "extensions"; // NOI18N |
| |
| /** */ |
| private Reference<PropertiesEncoding> encodingRef; |
| |
| /** */ |
| private static Set<String> knownLanguages; |
| /** */ |
| private static Set<String> knownCountries; |
| |
| /** */ |
| private static boolean nestedView = false; |
| |
| /** Creates new PropertiesDataLoader. */ |
| public PropertiesDataLoader() { |
| super("org.netbeans.modules.properties.PropertiesDataObject"); // NOI18N |
| |
| // Set extentions. Due performance reasons do it here instead in initialize method. |
| // During startup it's in findPrimaryFile method called getExtensions method. If the |
| // extentions list was not set in constructor the initialize method would be called |
| // during startup, but we want to avoid the initialize call since we don't need |
| // actions and display name initialized during startup time. |
| ExtensionList extList = new ExtensionList(); |
| extList.addExtension(PROPERTIES_EXTENSION); |
| // Add .impl for CORBA module. |
| extList.addExtension("impl"); // NOI18N |
| setExtensions(extList); |
| } |
| |
| /** |
| */ |
| PropertiesEncoding getEncoding() { |
| PropertiesEncoding encoding; |
| if ((encodingRef == null) || ((encoding = encodingRef.get()) == null)) { |
| encoding = new PropertiesEncoding(); |
| encodingRef = new SoftReference<PropertiesEncoding>(encoding); |
| } |
| return encoding; |
| } |
| |
| /** */ |
| @Override |
| protected String defaultDisplayName() { |
| return NbBundle.getMessage(PropertiesDataLoader.class, |
| "PROP_PropertiesLoader_Name"); //NOI18N |
| } |
| |
| /** |
| * This methods uses the layer action context so it returns |
| * a non-<code>null</code> value. |
| * |
| * @return name of the context on layer files to read/write actions to |
| */ |
| @Override |
| protected String actionsContext () { |
| return "Loaders/text/x-properties/Actions/"; //NOI18N |
| } |
| |
| /** |
| * @return <code>PropertiesDataObject</code> for the specified |
| * <code>FileObject</code> |
| */ |
| protected MultiDataObject createMultiObject(final FileObject fo) |
| throws IOException { |
| return new PropertiesDataObject(fo, this); |
| } |
| |
| /** */ |
| protected FileObject findPrimaryFile (FileObject fo) { |
| if (fo.isFolder()) { |
| return null; |
| } |
| if (fo.getExt().equalsIgnoreCase(PROPERTIES_EXTENSION)) { |
| |
| /* |
| * returns a file whose name is the shortest valid prefix |
| * corresponding to an existing file |
| */ |
| String fName = fo.getName(); |
| if (nestedView) { |
| int index = fName.indexOf(PRB_SEPARATOR_CHAR); |
| while (index != -1) { |
| FileObject candidate = fo.getParent().getFileObject( |
| fName.substring(0, index), fo.getExt()); |
| if (candidate != null && isValidLocaleSuffix(fName.substring(index))) { |
| return candidate; |
| } |
| index = fName.indexOf(PRB_SEPARATOR_CHAR, index + 1); |
| } |
| } |
| return fo; |
| } else if (getExtensions().isRegistered(fo)) { |
| return fo; |
| } else if (PROPERTIES_MIME_TYPE.equalsIgnoreCase(fo.getMIMEType(PROPERTIES_MIME_TYPE))) { |
| return fo; |
| } else { |
| return null; |
| } |
| } |
| |
| private static boolean isValidLocaleSuffix(String s) { |
| // first char is _ |
| int n = s.length(); |
| String s1; |
| // check first suffix - language (two chars) |
| if (n == 3 || (n > 3 && s.charAt(3) == PRB_SEPARATOR_CHAR)) { |
| s1 = s.substring(1, 3).toLowerCase(); |
| // language must be followed by a valid country suffix or no suffix |
| } else { |
| return false; |
| } |
| // check second suffix - country (two chars) |
| String s2; |
| if (n == 3) { |
| s2 = null; |
| } else if (n == 6 || (n > 6 && s.charAt(6) == PRB_SEPARATOR_CHAR)) { |
| s2 = s.substring(4, 6).toUpperCase(); |
| // country may be followed by whatever additional suffix |
| } else { |
| return false; |
| } |
| |
| if (knownLanguages == null) { |
| knownLanguages = new HashSet<String>(Arrays.asList(Locale.getISOLanguages())); |
| } |
| if (!knownLanguages.contains(s1)) { |
| return false; |
| } |
| |
| if (s2 != null) { |
| if (knownCountries == null) { |
| knownCountries = new HashSet<String>(Arrays.asList(Locale.getISOCountries())); |
| } |
| if (!knownCountries.contains(s2)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * @return <code>PropertiesFileEntry</code> for the given file |
| */ |
| protected MultiDataObject.Entry createPrimaryEntry(MultiDataObject obj, |
| FileObject primaryFile) { |
| return new PropertiesFileEntry(obj, primaryFile); |
| } |
| |
| /** |
| * @return <code>PropertiesFileEntry</code> for the given file |
| */ |
| protected MultiDataObject.Entry createSecondaryEntry( |
| MultiDataObject obj, |
| FileObject secondaryFile) { |
| return new PropertiesFileEntry(obj, secondaryFile); |
| } |
| |
| |
| /** |
| * Sets the extension list for this data loader. |
| * This data loader will then recognize all files having any extension |
| * of the given list. |
| * |
| * @param ext list of extensions |
| */ |
| public void setExtensions(ExtensionList ext) { |
| putProperty(PROP_EXTENSIONS, ext, true); |
| } |
| |
| /** |
| * Get the extension list for this data loader. |
| * |
| * @return list of extensions |
| * @see #setExtensions |
| */ |
| public ExtensionList getExtensions() { |
| ExtensionList l = (ExtensionList) getProperty(PROP_EXTENSIONS); |
| if (l == null) { |
| l = new ExtensionList(); |
| putProperty(PROP_EXTENSIONS, l, false); |
| } |
| return l; |
| } |
| |
| /** Writes extensions to the stream. |
| * @param oo ignored |
| */ |
| @Override |
| public void writeExternal (ObjectOutput oo) throws IOException { |
| super.writeExternal (oo); |
| |
| oo.writeObject (getProperty (PROP_EXTENSIONS)); |
| } |
| |
| /** Reads nothing from the stream. |
| * @param oi ignored |
| */ |
| @Override |
| public void readExternal (ObjectInput oi) |
| throws IOException, ClassNotFoundException { |
| SafeException se; |
| try { |
| super.readExternal (oi); |
| se = null; |
| } catch (SafeException se2) { |
| se = se2; |
| } |
| |
| setExtensions ((ExtensionList)oi.readObject ()); |
| if (se != null) { |
| throw se; |
| } |
| } |
| |
| } |