blob: d893a3895eca900385ea48b432680841176ee44f [file] [log] [blame]
/*
* 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.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.util.Exceptions;
/**
*
* @author alexeybutenko
*/
class MultiBundleStructure extends BundleStructure implements Serializable {
private transient FileObject[] files;
private transient FileObject parent;
private transient PropertiesFileEntry primaryEntry;
private String baseName;
private String extension;
/** Generated Serialized Version UID. */
static final long serialVersionUID = 7501232754255253334L;
private transient PropertiesOpen openSupport;
/** Lock used for synchronization of <code>openSupport</code> instance creation */
private final transient Object OPEN_SUPPORT_LOCK = new Object();
protected MultiBundleStructure() {
// super();
// files = null;
// primaryEntry = null;
// baseName = null;
}
public MultiBundleStructure(PropertiesDataObject obj) {
// super(obj);
this.obj = obj;
baseName = Util.getBaseName(obj.getName());
extension = PropertiesDataLoader.PROPERTIES_EXTENSION;
}
/**
* Find entries according to PropertiesDataObject
*
*/
private synchronized void findEntries() {
// try {
if (obj != null) {
if (!obj.isValid()) {
primaryEntry = null;
if (files!=null && files.length == 1) {
obj = null;
files = null;
return;
}
} else {
// obj = Util.findPrimaryDataObject(obj);
primaryEntry = (PropertiesFileEntry) obj.getPrimaryEntry();
}
} else {
return;
}
if (primaryEntry != null) {
FileObject primary = primaryEntry.getFile();
if(!primary.hasExt(extension)) {
if (primary.getMIMEType().equalsIgnoreCase(PropertiesDataLoader.PROPERTIES_MIME_TYPE))
extension = primary.getExt();
}
parent = primary.getParent();
} else {
if (parent == null) {
return;
}
}
List<FileObject> listFileObjects = new ArrayList<FileObject>();
String fName;
FileObject oldCandidate;
for (FileObject file : parent.getChildren()) {
if (!file.hasExt(extension) || !file.getMIMEType().equalsIgnoreCase(PropertiesDataLoader.PROPERTIES_MIME_TYPE)) {
continue;
}
fName = file.getName();
if (fName.equals(baseName) && file.isValid()) {
listFileObjects.add(0,file);
}
if (fName.indexOf(baseName) != -1) {
int index = fName.indexOf(PropertiesDataLoader.PRB_SEPARATOR_CHAR, baseName.length() - 1);
if (index == baseName.length()) {
if (file != null && isValidLocaleSuffix(fName.substring(index)) && file.isValid()) {
listFileObjects.add(file);
}
}
}
}
if (listFileObjects.isEmpty()) {
// a fallback if no other entries found
files = new FileObject[] {obj.getPrimaryFile()};
return;
}
files = listFileObjects.toArray(new FileObject[listFileObjects.size()]);
if (primaryEntry != getNthEntry(0)) {
//TODO XXX This means that primaryEntry has changed, so need to notify openSupport
primaryEntry = getNthEntry(0);
if (primaryEntry != null) {
notifyOneFileChanged(primaryEntry.getFile());
if(!primaryEntry.getFile().hasExt(extension)) {
if (primaryEntry.getFile().getMIMEType().equalsIgnoreCase(PropertiesDataLoader.PROPERTIES_MIME_TYPE))
extension = primaryEntry.getFile().getExt();
}
parent = primaryEntry.getFile().getParent();
obj = (PropertiesDataObject) primaryEntry.getDataObject();
baseName = Util.getBaseName(obj.getName());
}
}
// } catch (DataObjectNotFoundException ex) {
// Exceptions.printStackTrace(ex);
// }
}
@Override
void updateEntries() {
findEntries();
if (files != null) {
buildKeySet();
}
}
/**
* Moves entry from fromIndex to toIndex shifting the rest elements
* This method used in @see BundleEditPanel when switching columns order
* @param fromIndex
* @param toIndex
*/
void moveEntry(int fromIndex, int toIndex) {
if (fromIndex >= 0 && fromIndex < getEntryCount() && toIndex>=0 && toIndex < getEntryCount()) {
int sortIndex = getSortIndex();
if ((fromIndex+1) == sortIndex)
sortIndex = toIndex+1;
FileObject tmpFO = null;
if (fromIndex < toIndex) {
tmpFO = files[fromIndex];
for (int i=fromIndex; i<toIndex;i++) {
files[i] = files[i+1];
if (i == sortIndex-1) sortIndex--;
}
files[toIndex] = tmpFO;
} else if (fromIndex > toIndex) {
tmpFO = files[fromIndex];
for (int i=fromIndex;i>toIndex;i--) {
files[i] = files[i-1];
if(i == sortIndex-1) sortIndex++;
}
files[toIndex]=tmpFO;
}
if (getSortIndex()==toIndex+1) {
sortIndex = fromIndex+1;
}
if (sortIndex != getSortIndex()) {
boolean ascending = getSortOrder();
sort(sortIndex);
//preserving the sort order
if (!ascending)
sort(sortIndex);
}
}
}
@Override
public PropertiesFileEntry getNthEntry(int index) {
if (files == null) {
return null;//super.getNthEntry(index);
// notifyEntriesNotInitialized();
}
if (index >= 0 && index < files.length) {
try {
DataObject dataObject = DataObject.find(files[index]);
if (dataObject instanceof PropertiesDataObject) {
return (PropertiesFileEntry) ((PropertiesDataObject)dataObject).getPrimaryEntry();
}
} catch (DataObjectNotFoundException ex) {
// ex.printStackTrace();
}
}
return null;
}
/**
* Retrieves an index of a file entry representing the given file.
*
* @param fileName simple name (without path and extension) of the
* primary or secondary file
* @return index of the entry representing a file with the given filename;
* or <code>-1</code> if no such entry is found
* @exception java.lang.IllegalStateException
* if the list of entries has not been initialized yet
* @see #getEntryByFileName
*/
@Override
public int getEntryIndexByFileName(String fileName) {
if (files == null) {
// notifyEntriesNotInitialized();
return -1;
}
for (int i = 0; i < getEntryCount(); i++) {
if (files[i].getName().equals(fileName)) {
return i;
}
}
return -1;
}
/**
* Retrieves a file entry representing the given file
*
* @param fileName simple name (excl. path, incl. extension) of the
* primary or secondary file
* @return entry representing the given file;
* or <code>null</code> if not such entry is found
* @exception java.lang.IllegalStateException
* if the list of entries has not been initialized yet
* @see #getEntryIndexByFileName
*/
@Override
public PropertiesFileEntry getEntryByFileName(String fileName) {
int index = getEntryIndexByFileName(fileName);
try {
return (index == -1) ? null : (PropertiesFileEntry) ((PropertiesDataObject) DataObject.find(files[index])).getPrimaryEntry();
} catch (DataObjectNotFoundException ex) {
Exceptions.printStackTrace(ex);
return null;
}
}
/**
* Retrieves number of file entries.
*
* @return number of file entries
* @exception java.lang.IllegalStateException
* if the list of entries has not been initialized yet
*/
@Override
public int getEntryCount() {
if (files == null) {
return 0;//super.getEntryCount();
// notifyEntriesNotInitialized();
}
return files.length;
}
/**
* Throws a runtime exception with a message that the entries
* have not been initialized yet.
*
* @exception java.lang.IllegalStateException thrown always
* @see #updateEntries
*/
private void notifyEntriesNotInitialized() {
throw new IllegalStateException(
"Resource Bundles: Entries not initialized"); //NOI18N
}
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) == PropertiesDataLoader.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) == PropertiesDataLoader.PRB_SEPARATOR_CHAR)) {
s2 = s.substring(4, 6).toUpperCase();
// country may be followed by whatever additional suffix
} else {
return false;
}
Set<String> knownLanguages = new HashSet<String>(Arrays.asList(Locale.getISOLanguages()));
if (!knownLanguages.contains(s1)) {
return false;
}
if (s2 != null) {
Set<String> knownCountries = new HashSet<String>(Arrays.asList(Locale.getISOCountries()));
if (!knownCountries.contains(s2)) {
return false;
}
}
return true;
}
@Override
public PropertiesOpen getOpenSupport() {
synchronized (OPEN_SUPPORT_LOCK) {
if (openSupport == null) {
openSupport = new PropertiesOpen(this);
}
return openSupport;
}
}
@Override
public int getKeyCount() {
try {
return super.getKeyCount();
} catch (IllegalStateException ie) {
return 0;
}
}
@Override
PropertiesFileEntry[] getEntries() {
synchronized (this) {
if (files == null) {
return new PropertiesFileEntry[0];
} else {
ArrayList<PropertiesFileEntry> entries = new ArrayList<PropertiesFileEntry>(files.length);
try {
for (FileObject fileObject : files) {
DataObject dataObject = DataObject.find(fileObject);
if (dataObject instanceof PropertiesDataObject) {
entries.add((PropertiesFileEntry) ((PropertiesDataObject) dataObject).getPrimaryEntry());
}
}
} catch (DataObjectNotFoundException ex) {
// ex.printStackTrace();
}
if(entries.isEmpty()){
return new PropertiesFileEntry[0];
} else {
return entries.toArray(new PropertiesFileEntry[entries.size()]);
}
}
}
}
}