| /* |
| * 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.i18n.form; |
| |
| import java.awt.Component; |
| import java.awt.Dialog; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.beans.PropertyEditor; |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URL; |
| import java.util.*; |
| |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import org.openide.ErrorManager; |
| import org.openide.DialogDisplayer; |
| import org.openide.filesystems.FileObject; |
| import org.openide.loaders.*; |
| import org.openide.cookies.SaveCookie; |
| import org.openide.DialogDescriptor; |
| import org.openide.util.NbBundle; |
| import org.netbeans.api.java.classpath.ClassPath; |
| import org.netbeans.api.java.project.JavaProjectConstants; |
| import org.netbeans.api.project.FileOwnerQuery; |
| import org.netbeans.api.project.Project; |
| import org.netbeans.api.project.ProjectUtils; |
| import org.netbeans.api.project.SourceGroup; |
| import org.netbeans.api.project.Sources; |
| |
| import org.netbeans.modules.properties.*; |
| import org.netbeans.modules.i18n.*; |
| import org.netbeans.modules.i18n.java.JavaResourceHolder; |
| |
| import org.netbeans.modules.form.I18nService; |
| import org.netbeans.modules.form.I18nValue; |
| import org.openide.filesystems.FileUtil; |
| |
| /** |
| * Implementation of form module's I18nService - used by form editor to control |
| * internationalization of forms while i18n module owns all the technical means |
| * (i18n values, property editors, bundle files). |
| */ |
| @org.openide.util.lookup.ServiceProvider(service=org.netbeans.modules.form.I18nService.class) |
| public class I18nServiceImpl implements I18nService { |
| |
| // remembered original state for changes made for given source data objects |
| // mapping source DO to a map of properties DO to ChangeInfo |
| private Map/*<DataObject, Map<DataObject, ChangeInfo>>*/ changesMap = new HashMap(); |
| |
| private static class ChangeInfo { |
| Map/*<String, Object[]>*/ changed = new HashMap(); // for each key holds all data across all locales |
| Set/*<String>*/ added = new HashSet(); // holds keys that were not originally present |
| } |
| |
| /** |
| * Creates I18nValue object for given key and value. Should not be added |
| * to the bundle file yet. (For that purpose 'update' method is called later.) |
| */ |
| @Override |
| public I18nValue create(String key, String value, DataObject srcDataObject) { |
| FormI18nString i18nString = new FormI18nString(srcDataObject); |
| i18nString.setKey(key); |
| i18nString.setValue(value); |
| return i18nString; |
| } |
| |
| /** |
| * Creates a copy of I18nValue, including data from all locales corresponding |
| * to the actual key. The copied value does not refer to the original |
| * properties file - i.e. can be added to another one. |
| * @param value I18nValue to be copied |
| * @return the copied I18nValue |
| */ |
| @Override |
| public I18nValue copy(I18nValue value) { |
| FormI18nString i18nString = (FormI18nString) value; |
| FormI18nString copy = new FormI18nString(i18nString); |
| copy.getSupport().getResourceHolder().setResource(null); |
| if (i18nString.allData == null && i18nString.getKey() != null) { |
| JavaResourceHolder jrh = (JavaResourceHolder) i18nString.getSupport().getResourceHolder(); |
| copy.allData = jrh.getAllData(i18nString.getKey()); |
| } else { |
| copy.allData = i18nString.allData; |
| } |
| return copy; |
| } |
| |
| /** |
| * Creates a new I18nValue object with a new key. Should do no changes to |
| * the bundle file at this moment. |
| */ |
| @Override |
| public I18nValue changeKey(I18nValue prev, String newKey) { |
| FormI18nString oldI18nString = (FormI18nString) prev; |
| FormI18nString changedI18nString; |
| if (oldI18nString.getKey() == I18nValue.COMPUTE_AUTO_KEY) { |
| // set key which was unset so far |
| changedI18nString = oldI18nString; |
| } |
| else { // create a new value for the new key |
| changedI18nString = new FormI18nString(oldI18nString); |
| changedI18nString.allData = oldI18nString.allData; |
| } |
| changedI18nString.setKey(newKey); |
| return changedI18nString; |
| } |
| |
| /** |
| * Creates a new I18nValue object with changed value. Should not do any |
| * changes to the bundle file. |
| */ |
| @Override |
| public I18nValue changeValue(I18nValue prev, String value) { |
| FormI18nString i18nString = new FormI18nString((FormI18nString)prev); |
| i18nString.setValue(value); |
| return i18nString; |
| } |
| |
| /** |
| * Creates a new I18nValue refering to given locale (both for reading and |
| * writing from now). |
| */ |
| @Override |
| public I18nValue switchLocale(I18nValue value, String localeSuffix) { |
| if (value == null || value.getKey() == null) |
| return value; |
| |
| FormI18nString i18nString; |
| if (value instanceof FormI18nInteger) { |
| i18nString = new FormI18nInteger((FormI18nInteger)value); |
| } else if (value instanceof FormI18nMnemonic) { |
| i18nString = new FormI18nMnemonic((FormI18nMnemonic)value); |
| } else { |
| i18nString = new FormI18nString((FormI18nString)value); |
| } |
| JavaResourceHolder rh = (JavaResourceHolder) i18nString.getSupport().getResourceHolder(); |
| rh.setLocalization(localeSuffix); |
| i18nString.setValue(rh.getValueForKey(i18nString.getKey())); |
| i18nString.setComment(rh.getCommentForKey(i18nString.getKey())); |
| return i18nString; |
| } |
| |
| /** |
| * Updates bundle file according to given I18nValue objects - oldValue is |
| * removed, newValue added. Update goes into given locale - parent files |
| * are updated too if given key is not present in them. New properties file |
| * is created if needed. |
| */ |
| @Override |
| public void update(I18nValue oldValue, I18nValue newValue, |
| DataObject srcDataObject, String bundleName, String localeSuffix, |
| boolean canRemove) |
| throws IOException |
| { |
| FormI18nString oldI18nString = (FormI18nString) oldValue; |
| FormI18nString newI18nString = (FormI18nString) newValue; |
| |
| if (oldI18nString != null) { |
| ResourceHolder oldRH = oldI18nString.getSupport().getResourceHolder(); |
| DataObject oldRes = oldRH.getResource(); |
| DataObject newRes = null; |
| if (newI18nString != null) { |
| ResourceHolder newRH = newI18nString.getSupport().getResourceHolder(); |
| newRes = newRH.getResource(); |
| if (newRes == null) { // use same resource bundle as old value |
| newRH.setResource(oldRes); |
| newRes = oldRes; |
| } |
| } |
| |
| String oldKey = oldI18nString.getKey(); |
| if (canRemove && oldKey != null) { |
| JavaResourceHolder jrh = (JavaResourceHolder) oldRH; |
| Object allData = jrh.getAllData(oldKey); |
| registerChange(srcDataObject, oldRes, oldKey, allData); |
| |
| if (newI18nString == null |
| || newI18nString.getKey() == null |
| || !newI18nString.getKey().equals(oldKey) |
| || newRes != oldRes) |
| { // removing i18n value, changing key, or moving to another properties file |
| oldI18nString.allData = allData; |
| jrh.removeProperty(oldKey); |
| if (newI18nString != null) |
| newI18nString.allData = oldI18nString.allData; |
| } |
| else if (localeSuffix != null && !localeSuffix.equals("")) { // NOI18N |
| // remember all locale data (to be able to undo adding new specific value to a locale) |
| oldI18nString.allData = allData; |
| } |
| |
| if (newI18nString == null |
| && oldRes == getPropertiesDataObject(srcDataObject.getPrimaryFile(), bundleName)) |
| { // forget the resource bundle file - may want different next time |
| oldRH.setResource(null); |
| } |
| } |
| } |
| |
| if (newI18nString != null && newI18nString.getKey() != null) { |
| // valid new value - make sure it is up-to-date in the properties file |
| JavaResourceHolder rh = (JavaResourceHolder) newI18nString.getSupport().getResourceHolder(); |
| String key = newI18nString.getKey(); |
| |
| if (rh.getResource() == null) { // find or create properties file |
| DataObject propertiesDO = getPropertiesDataObject(srcDataObject.getPrimaryFile(), bundleName); |
| if (propertiesDO == null) { // create new properties file |
| propertiesDO = createPropertiesDataObject(srcDataObject.getPrimaryFile(), bundleName); |
| if (propertiesDO == null) |
| return; |
| } else if (oldI18nString == null && newI18nString.getValue() == null) { |
| // if the value itself is null we actually want to update it from the properties file |
| rh.setResource(propertiesDO); |
| newI18nString.setValue(rh.getValueForKey(key)); |
| newI18nString.setComment(rh.getCommentForKey(key)); |
| return; |
| } |
| rh.setResource(propertiesDO); |
| |
| // make sure we use free (unique) key |
| key = rh.findFreeKey(key); |
| newI18nString.setKey(key); |
| } |
| |
| rh.setLocalization(localeSuffix); |
| if (!isValueUpToDate(rh, newI18nString)) { |
| if (newI18nString.allData != null) { // restore complete data across all locales |
| if (oldI18nString != null && newI18nString.getValue() != null) { |
| // besides changing place (key/file) there might also be a new value/comment |
| updateAllData(newI18nString, localeSuffix); |
| } |
| rh.setAllData(key, newI18nString.allData); |
| newI18nString.allData = null; |
| if (oldI18nString == null) { |
| // update also the current value - might have come from a different locale |
| newI18nString.setValue(rh.getValueForKey(key)); |
| newI18nString.setComment(rh.getCommentForKey(key)); |
| } |
| } |
| else { |
| rh.addProperty(key, newI18nString.getValue(), newI18nString.getComment(), true); |
| } |
| registerChange(srcDataObject, rh.getResource(), newI18nString.getKey(), null); |
| } |
| } |
| } |
| |
| private static boolean isValueUpToDate(ResourceHolder rh, I18nString i18nString) { |
| String storedValue = rh.getValueForKey(i18nString.getKey()); |
| String storedComment = rh.getCommentForKey(i18nString.getKey()); |
| if ("".equals(storedComment)) // NOI18N |
| storedComment = null; |
| String newValue = i18nString.getValue(); |
| String newComment = i18nString.getComment(); |
| if ("".equals(newComment)) // NOI18N |
| newComment = null; |
| return (storedValue == newValue || (storedValue != null && storedValue.equals(newValue))) |
| && (storedComment == newComment || (storedComment != null && storedComment.equals(newComment))); |
| } |
| |
| private static void updateAllData(FormI18nString newI18nString, String localeSuffix) { |
| // Not nice we deal with the data format that is internal to the properties |
| // module, but need to workaround bug 240650 somehow - by applying all the changes |
| // at once. Trying to modify the added item subsequently might happen when the |
| // PropertiesStructure is in an inconsistent state and add the item for the second time. |
| String[] allData = (newI18nString.allData instanceof String[]) ? (String[]) newI18nString.allData : null; |
| if (allData != null) { |
| for (int i=0; i < allData.length; i+=3) { |
| String locale = allData[i]; |
| if (localeSuffix.equals(locale)) { |
| allData[i+1] = newI18nString.getValue(); |
| allData[i+2] = newI18nString.getComment(); |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns property editor to be used for editing internationalized |
| * property of given type (e.g. String). If an existing suitable editor is |
| * passed then it is returned and no new property editor is created. |
| */ |
| @Override |
| public PropertyEditor getPropertyEditor(Class type, PropertyEditor existing) { |
| return existing instanceof FormI18nStringEditor ? existing : new FormI18nStringEditor(); |
| } |
| |
| private static boolean isPlainStringEditor(PropertyEditor pe) { |
| return pe != null && pe.getClass().getName().endsWith(".StringEditor"); // NOI18N |
| } |
| |
| /** |
| * Provides a component usable as property customizer (so typically a modal |
| * dialog) that allows to choose (or create) a properties bundle file within |
| * the project of given form data object. The selected file should be |
| * written to the given property editor (via setValue) as a resource name |
| * string. |
| */ |
| @Override |
| public Component getBundleSelectionComponent(final PropertyEditor prEd, FileObject srcFile) { |
| try { |
| final FileSelector fs = new FileSelector(srcFile, JavaResourceHolder.getTemplate()); |
| return fs.getDialog(NbBundle.getMessage(I18nServiceImpl.class, "CTL_SELECT_BUNDLE_TITLE"), // NOI18N |
| new ActionListener() |
| { |
| @Override |
| public void actionPerformed(ActionEvent ev) { |
| DataObject bundleDO = fs.getSelectedDataObject(); |
| if (bundleDO != null) { |
| ClassPath cp = ClassPath.getClassPath(bundleDO.getPrimaryFile(), ClassPath.SOURCE); |
| if (cp != null) { |
| String bundleName = cp.getResourceName(bundleDO.getPrimaryFile(), '/', false); |
| prEd.setValue(bundleName); |
| } |
| } |
| } |
| }); |
| } |
| catch (IOException ex) { |
| // means that template for properties file was not found - unlikely |
| ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns all currently available locales for given bundle in two arrays |
| * of strings. The first one containes locale suffixes, the second one |
| * corresponding display names for the user (should be unique). |
| * Returning null means that working with design locales is not supported |
| * by this service. |
| */ |
| @Override |
| public String[][] getAvailableLocales(FileObject srcFile, String bundleName) { |
| PropertiesDataObject dobj = null; |
| try { |
| dobj = getPropertiesDataObject(srcFile, bundleName); |
| } |
| catch (IOException ex) { |
| ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); |
| } |
| if (dobj == null) { |
| return new String[][] {{},null}; |
| } |
| |
| List list = new ArrayList(); |
| list.add(dobj.getPrimaryEntry()); |
| list.addAll(dobj.secondaryEntries()); |
| try { |
| String baseName = dobj.getName() + "_"; // NOI18N |
| for (FileObject fo : dobj.getPrimaryFile().getParent().getChildren()) { |
| String fileName = fo.getNameExt(); |
| if (fileName.endsWith(".properties") && fileName.startsWith(baseName)) { // NOI18N |
| DataObject dobj2 = DataObject.find(fo); |
| if (dobj2 instanceof PropertiesDataObject) { |
| list.add(((MultiDataObject)dobj2).getPrimaryEntry()); |
| } |
| } |
| } |
| } catch (IOException ex) { |
| ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); |
| } |
| Collections.sort(list, new Comparator() { |
| @Override |
| public int compare(Object o1, Object o2) { |
| MultiDataObject.Entry e1 = (MultiDataObject.Entry) o1; |
| MultiDataObject.Entry e2 = (MultiDataObject.Entry) o2; |
| return e1.getFile().getName().compareTo(e2.getFile().getName()); |
| } |
| }); |
| |
| String[] locales = new String[list.size()]; |
| String[] displays = new String[list.size()]; |
| for (int i=0; i < list.size(); i++) { |
| MultiDataObject.Entry entry = (MultiDataObject.Entry) list.get(i); |
| locales[i] = org.netbeans.modules.properties.Util.getLocaleSuffix(entry); |
| displays[i] = org.netbeans.modules.properties.Util.getLocaleLabel(entry); |
| } |
| return new String[][] { locales, displays }; |
| } |
| |
| /** |
| * Provides a visual component (modal dialog) usable as a property |
| * customizer that allows create a new locale file for given bundle (default |
| * bundle name provided). The created locale should be written as a string |
| * (locale suffix) to the given propery editor. |
| */ |
| @Override |
| public Component getCreateLocaleComponent(final PropertyEditor prEd, FileObject srcFile, String bundleName) { |
| final PropertiesDataObject propertiesDO; |
| try { |
| propertiesDO = getPropertiesDataObject(srcFile, bundleName); |
| } |
| catch (DataObjectNotFoundException ex) { |
| ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); |
| return null; |
| } |
| final Dialog[] dialog = new Dialog[1]; |
| final LocalePanel localePanel = new LocalePanel(); |
| |
| DialogDescriptor dialogDescriptor = new DialogDescriptor( |
| localePanel, |
| NbBundle.getBundle(PropertiesDataObject.class).getString("CTL_NewLocaleTitle"), // NOI18N |
| true, |
| DialogDescriptor.OK_CANCEL_OPTION, |
| DialogDescriptor.OK_OPTION, |
| new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent evt) { |
| if (evt.getSource() == DialogDescriptor.OK_OPTION) { |
| String locale = localePanel.getLocale().toString(); |
| org.netbeans.modules.properties.Util.createLocaleFile( |
| propertiesDO, locale, false); |
| prEd.setValue("_" + locale); // NOI18N |
| } |
| dialog[0].setVisible(false); |
| dialog[0].dispose(); |
| } |
| } |
| ); |
| dialog[0] = DialogDisplayer.getDefault().createDialog(dialogDescriptor); |
| return dialog[0]; |
| } |
| |
| /** |
| * Saves properties files edited for given source object (form). This method |
| * is called when a form is being saved - so the corresponding bundle is |
| * saved as well. |
| */ |
| @Override |
| public void autoSave(DataObject srcDataObject) { |
| Map/*<DataObject, ChangeInfo>*/ relatedMap = (Map) changesMap.remove(srcDataObject); |
| if (relatedMap != null) { |
| for (Iterator it=relatedMap.keySet().iterator(); it.hasNext(); ) { |
| DataObject propertiesDO = (DataObject) it.next(); |
| // [not sure: should we auto-save only bundles not opened in the editor? |
| // perhaps it's OK to save always...] |
| SaveCookie save = (SaveCookie) propertiesDO.getCookie(SaveCookie.class); |
| if (save != null) { |
| try { |
| save.save(); |
| } |
| catch (IOException ex) { |
| ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Called when a form is closed without saving changes. The changes in |
| * corresponding properties file need to be discarded (reverted) as well. |
| */ |
| @Override |
| public void close(DataObject srcDataObject) { |
| Map/*<DataObject, ChangeInfo>*/ relatedMap = (Map) changesMap.remove(srcDataObject); |
| if (relatedMap != null) { |
| for (Iterator it=relatedMap.entrySet().iterator(); it.hasNext(); ) { |
| Map.Entry/*<DataObject, ChangeInfo>*/ e = (Map.Entry) it.next(); |
| PropertiesDataObject propertiesDO = (PropertiesDataObject) e.getKey(); |
| ChangeInfo changes = (ChangeInfo) e.getValue(); |
| JavaResourceHolder rh = new JavaResourceHolder(); |
| rh.setResource(propertiesDO); |
| for (Iterator/*<Map.Entry<String, Object>>*/ it2=changes.changed.entrySet().iterator(); it2.hasNext(); ) { |
| Map.Entry/*<String, Object>*/ e2 = (Map.Entry) it2.next(); |
| String key = (String) e2.getKey(); |
| Object allData = e2.getValue(); |
| rh.setAllData(key, allData); |
| } |
| for (Iterator it2=changes.added.iterator(); it2.hasNext(); ) { |
| String key = (String) it2.next(); |
| rh.removeProperty(key); |
| } |
| // [not sure: should we save the bundle for consistency? |
| // perhaps not necessary...] |
| // SaveCookie save = (SaveCookie) propertiesDO.getCookie(SaveCookie.class); |
| // if (save != null) { |
| // try { |
| // save.save(); |
| // } |
| // catch (IOException ex) { |
| // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); |
| // } |
| // } |
| } |
| } |
| } |
| |
| /** |
| * Checks project of given form whether it is suitable to be automatically |
| * internationalized by default. Currently new forms in module projects |
| * should be set to auto i18n, while standard user (J2SE) projects not. |
| * [If we decide all projects should be internationalized, we can remove |
| * this method.] |
| */ |
| @Override |
| public boolean isDefaultInternationalizableProject(FileObject srcFile) { |
| return isNbBundleAvailable(srcFile); |
| } |
| |
| static boolean isNbBundleAvailable(FileObject srcFile) { |
| // is there a good way to recognize that NbBundle is available? |
| // - execution CP may not work if everything is cleaned |
| // - looking for NbBundle.java in sources of execution CP roots is expensive |
| // - checking project impl. class name is ugly |
| // - don't know how to check if there is "org.openide.util" module |
| ClassPath classPath = ClassPath.getClassPath(srcFile, ClassPath.EXECUTE); |
| if (classPath != null && classPath.findResource("org/openide/util/NbBundle.class") != null) // NOI18N |
| return true; |
| |
| // hack: check project impl. class name |
| Project p = FileOwnerQuery.getOwner(srcFile); |
| if (p != null && p.getClass().getName().startsWith("org.netbeans.modules.apisupport.") // NOI18N |
| && p.getClass().getName().endsWith("Project")) // NOI18N |
| return true; |
| |
| return false; |
| } |
| |
| @Override |
| public List<URL> getResourceFiles(FileObject srcFile, String bundleName) { |
| PropertiesDataObject dobj = null; |
| try { |
| dobj = getPropertiesDataObject(srcFile, bundleName); |
| if (dobj != null) { |
| List<URL> list = new ArrayList<URL>(); |
| list.add(dobj.getPrimaryEntry().getFile().toURL()); |
| for (MultiDataObject.Entry e : dobj.secondaryEntries()) { |
| list.add(e.getFile().toURL()); |
| } |
| return list; |
| } else { |
| FileObject root = getResourcesRoot(srcFile); |
| if (root != null) { |
| return Collections.singletonList( |
| new File(FileUtil.toFile(root).getPath() |
| + File.separator + bundleName + ".properties").toURI().toURL()); // NOI18N |
| } |
| } |
| } catch (IOException ex) { |
| Logger.getLogger(I18nServiceImpl.class.getName()).log(Level.INFO, null, ex); // NOI18N |
| } |
| return Collections.emptyList(); |
| } |
| |
| // ----- |
| |
| private static PropertiesDataObject getPropertiesDataObject(FileObject srcFile, String bundleName) |
| throws DataObjectNotFoundException |
| { |
| if (bundleName.startsWith("/")) // NOI18N |
| bundleName = bundleName.substring(1); |
| if (!bundleName.toLowerCase().endsWith(".properties")) // NOI18N |
| bundleName = bundleName + ".properties"; // NOI18N |
| FileObject bundleFile = org.netbeans.modules.i18n.Util.getResource(srcFile, bundleName); |
| if (bundleFile != null) { |
| DataObject dobj = DataObject.find(bundleFile); |
| if (dobj instanceof PropertiesDataObject) |
| return (PropertiesDataObject) dobj; |
| } |
| return null; |
| } |
| |
| private static DataObject createPropertiesDataObject(FileObject srcFile, |
| String filePath) |
| throws IOException |
| { |
| if (filePath == null) { |
| return null; |
| } |
| |
| FileObject root = getResourcesRoot(srcFile); |
| return org.netbeans.modules.properties.Util.createPropertiesDataObject(root, filePath); |
| } |
| |
| private static FileObject getResourcesRoot(FileObject srcFile) { |
| FileObject root = null; |
| Project owner = FileOwnerQuery.getOwner(srcFile); |
| if (owner != null) { |
| // this is for projects that have split sources/resources folder structures. |
| Sources srcs = ProjectUtils.getSources(owner); |
| SourceGroup[] grps = srcs.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_RESOURCES); |
| if (grps != null && grps.length > 0) { |
| root = grps[0].getRootFolder(); |
| } |
| } |
| if (root == null) { |
| root = ClassPath.getClassPath(srcFile, ClassPath.SOURCE).getRoots()[0]; |
| } |
| return root; |
| } |
| |
| /** |
| * Keeps original data from properties file when first change is done in |
| * given properties file for given source file (form). If the source file |
| * is discarded later, all relevant changes in the properties file are reverted. |
| */ |
| private void registerChange(DataObject srcDO, DataObject propertiesDO, String key, Object allData) { |
| if (propertiesDO == null) { |
| return; |
| } |
| Map/*<DataObject, ChangeInfo>*/ relatedMap = (Map) changesMap.get(srcDO); |
| if (relatedMap == null) { |
| relatedMap = new HashMap(); |
| changesMap.put(srcDO, relatedMap); |
| } |
| ChangeInfo changes = (ChangeInfo) relatedMap.get(propertiesDO); |
| if (changes == null) { |
| changes = new ChangeInfo(); |
| relatedMap.put(propertiesDO, changes); |
| } |
| if (!changes.changed.containsKey(key) && !changes.added.contains(key)) { |
| // original state of this key not registered yet |
| if (allData != null) // something changed in existing data |
| changes.changed.put(key, allData); // allData contains original data |
| else // new key added |
| changes.added.add(key); |
| } |
| } |
| } |