001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.mercurial.config;
043:
044: import java.io.File;
045: import java.io.FileNotFoundException;
046: import java.io.FileReader;
047: import java.io.BufferedOutputStream;
048: import java.io.FileOutputStream;
049: import java.io.IOException;
050: import java.util.Iterator;
051: import java.util.Set;
052: import java.util.Properties;
053: import java.util.logging.Level;
054: import org.ini4j.Ini;
055: import org.netbeans.modules.mercurial.Mercurial;
056: import org.openide.filesystems.FileUtil;
057: import org.openide.util.Utilities;
058:
059: /**
060: *
061: * Handles the Mercurial <b>hgrc</b> configuration file.</br>
062: *
063: * @author Padraig O'Briain
064: */
065: public class HgConfigFiles {
066: public static final String HG_EXTENSIONS = "extensions"; // NOI18N
067: public static final String HG_EXTENSIONS_HGK = "hgext.hgk"; // NOI18N
068: public static final String HG_EXTENSIONS_FETCH = "fetch"; // NOI18N
069: public static final String HG_UI_SECTION = "ui"; // NOI18N
070: public static final String HG_USERNAME = "username"; // NOI18N
071: public static final String HG_PATHS_SECTION = "paths"; // NOI18N
072: public static final String HG_DEFAULT_PUSH = "default-push"; // NOI18N
073: public static final String HG_DEFAULT_PUSH_VALUE = "default-push"; // NOI18N
074: public static final String HG_DEFAULT_PULL = "default-pull"; // NOI18N
075: public static final String HG_DEFAULT_PULL_VALUE = "default"; // NOI18N
076:
077: /** The HgConfigFiles instance for user and system defaults */
078: private static HgConfigFiles instance;
079:
080: /** the Ini instance holding the configuration values stored in the <b>hgrc</b>
081: * file used by the Mercurial module */
082: private Ini hgrc = null;
083:
084: /** The repository directory if this instance is for a repository */
085: private File dir;
086: private static final String WINDOWS_USER_APPDATA = getAPPDATA();
087: private static final String WINDOWS_CONFIG_DIR = WINDOWS_USER_APPDATA
088: + "\\Mercurial"; // NOI18N
089: private static final String WINDOWS_GLOBAL_CONFIG_DIR = getGlobalAPPDATA()
090: + "\\Mercurial"; // NOI18N
091: public static final String HG_RC_FILE = "hgrc"; // NOI18N
092: public static final String HG_REPO_DIR = ".hg"; // NOI18N
093:
094: /**
095: * Creates a new instance
096: */
097: private HgConfigFiles() {
098: // get the system hgrc file
099: hgrc = loadFile(HG_RC_FILE);
100: }
101:
102: /**
103: * Returns a singleton instance
104: *
105: * @return the HgConfiles instance
106: */
107: public static HgConfigFiles getInstance() {
108: if (instance == null) {
109: instance = new HgConfigFiles();
110: }
111: return instance;
112: }
113:
114: public HgConfigFiles(File file) {
115: dir = file;
116: hgrc = loadFile(file, HG_RC_FILE);
117: }
118:
119: public void setProperty(String name, String value) {
120: if (name.equals(HG_USERNAME)) {
121: setProperty(HG_UI_SECTION, HG_USERNAME, value);
122: } else if (name.equals(HG_DEFAULT_PUSH)) {
123: setProperty(HG_PATHS_SECTION, HG_DEFAULT_PUSH_VALUE, value);
124: } else if (name.equals(HG_DEFAULT_PULL)) {
125: setProperty(HG_PATHS_SECTION, HG_DEFAULT_PULL_VALUE, value);
126: } else if (name.equals(HG_EXTENSIONS_HGK)) {
127: // Allow hgext.hgk to be set to some other user defined value if required
128: if (getProperty(HG_EXTENSIONS, HG_EXTENSIONS_HGK)
129: .equals("")) {
130: setProperty(HG_EXTENSIONS, HG_EXTENSIONS_HGK, value,
131: true);
132: }
133: } else if (name.equals(HG_EXTENSIONS_FETCH)) {
134: // Allow fetch to be set to some other user defined value if required
135: if (getProperty(HG_EXTENSIONS, HG_EXTENSIONS_FETCH).equals(
136: "")) {
137: setProperty(HG_EXTENSIONS, HG_EXTENSIONS_FETCH, value,
138: true);
139: }
140: }
141:
142: }
143:
144: public void setProperty(String section, String name, String value,
145: boolean allowEmpty) {
146: if (!allowEmpty) {
147: if (value.length() == 0) {
148: removeProperty(section, name);
149: } else {
150: Ini.Section inisection = getSection(hgrc, section, true);
151: inisection.put(name, value);
152: }
153: } else {
154: Ini.Section inisection = getSection(hgrc, section, true);
155: inisection.put(name, value);
156: }
157: storeIni(hgrc, HG_RC_FILE);
158: }
159:
160: public void setProperty(String section, String name, String value) {
161: setProperty(section, name, value, false);
162: }
163:
164: public void setUserName(String value) {
165: setProperty(HG_UI_SECTION, HG_USERNAME, value);
166: }
167:
168: public String getUserName() {
169: return getUserName(true);
170: }
171:
172: public Properties getProperties(String section) {
173: Ini.Section inisection = getSection(hgrc, section, false);
174: Properties props = new Properties();
175: if (inisection != null) {
176: Set<String> keys = inisection.keySet();
177: for (String key : keys) {
178: props.setProperty(key, inisection.get(key));
179: }
180: }
181: return props;
182: }
183:
184: public void clearProperties(String section) {
185: Ini.Section inisection = getSection(hgrc, section, false);
186: if (inisection != null) {
187: inisection.clear();
188: storeIni(hgrc, HG_RC_FILE);
189: }
190: }
191:
192: public void removeProperty(String section, String name) {
193: Ini.Section inisection = getSection(hgrc, section, false);
194: if (inisection != null) {
195: inisection.remove(name);
196: storeIni(hgrc, HG_RC_FILE);
197: }
198: }
199:
200: public String getDefaultPull(Boolean reload) {
201: if (reload) {
202: doReload();
203: }
204: return getProperty(HG_PATHS_SECTION, HG_DEFAULT_PULL_VALUE);
205: }
206:
207: public String getDefaultPush(Boolean reload) {
208: if (reload) {
209: doReload();
210: }
211: String value = getProperty(HG_PATHS_SECTION, HG_DEFAULT_PUSH);
212: if (value.length() == 0) {
213: value = getProperty(HG_PATHS_SECTION, HG_DEFAULT_PULL_VALUE);
214: }
215: return value;
216: }
217:
218: public String getUserName(Boolean reload) {
219: if (reload) {
220: doReload();
221: }
222: return getProperty(HG_UI_SECTION, HG_USERNAME);
223: }
224:
225: public String getProperty(String section, String name) {
226: Ini.Section inisection = getSection(hgrc, section, true);
227: String value = inisection.get(name);
228: return value != null ? value : ""; // NOI18N
229: }
230:
231: public boolean containsProperty(String section, String name) {
232: Ini.Section inisection = getSection(hgrc, section, true);
233: return inisection.containsKey(name);
234: }
235:
236: private void doReload() {
237: if (dir == null) {
238: hgrc = loadFile(HG_RC_FILE);
239: } else {
240: hgrc = loadFile(dir, HG_RC_FILE);
241: }
242: }
243:
244: private Ini.Section getSection(Ini ini, String key, boolean create) {
245: Ini.Section section = ini.get(key);
246: if (section == null && create) {
247: return ini.add(key);
248: }
249: return section;
250: }
251:
252: private void storeIni(Ini ini, String iniFile) {
253: try {
254: String filePath;
255: if (dir != null) {
256: filePath = dir.getAbsolutePath() + File.separator
257: + HG_REPO_DIR + File.separator + iniFile; // NOI18N
258: } else {
259: filePath = getUserConfigPath() + iniFile;
260: }
261: File file = FileUtil.normalizeFile(new File(filePath));
262: file.getParentFile().mkdirs();
263: ini.store(new BufferedOutputStream(new FileOutputStream(
264: file)));
265: } catch (IOException ex) {
266: Mercurial.LOG.log(Level.INFO, null, ex);
267: }
268: }
269:
270: /**
271: * Returns the path for the Mercurial configuration directory
272: *
273: * @return the path
274: *
275: */
276: public static String getUserConfigPath() {
277: if (Utilities.isUnix()) {
278: String path = System.getProperty("user.home"); // NOI18N
279: return path + "/."; // NOI18N
280: } else if (Utilities.isWindows()) {
281: return WINDOWS_CONFIG_DIR + "/"; // NOI18N
282: }
283: return ""; // NOI18N
284: }
285:
286: private Ini loadFile(File dir, String fileName) {
287: String filePath = dir.getAbsolutePath() + File.separator
288: + HG_REPO_DIR + File.separator + fileName; // NOI18N
289: File file = FileUtil.normalizeFile(new File(filePath));
290: Ini system = null;
291: try {
292: system = new Ini(new FileReader(file));
293: } catch (FileNotFoundException ex) {
294: // ignore
295: } catch (IOException ex) {
296: Mercurial.LOG.log(Level.INFO, null, ex);
297: }
298:
299: if (system == null) {
300: system = new Ini();
301: Mercurial.LOG.log(Level.WARNING, "Could not load the file "
302: + filePath + ". Falling back on hg defaults."); // NOI18N
303: }
304: return system;
305: }
306:
307: /**
308: * Loads the configuration file
309: * The settings are loaded and merged together in the folowing order:
310: * <ol>
311: * <li> The per-user configuration file, i.e ~/.hgrc
312: * <li> The system-wide file, i.e. /etc/mercurial/hgrc
313: * </ol>
314: *
315: * @param fileName the file name
316: * @return an Ini instance holding the configuration file.
317: */
318: private Ini loadFile(String fileName) {
319: // config files from userdir
320: String filePath = getUserConfigPath() + fileName;
321: File file = FileUtil.normalizeFile(new File(filePath));
322: Ini system = null;
323: try {
324: system = new Ini(new FileReader(file));
325: } catch (FileNotFoundException ex) {
326: // ignore
327: } catch (IOException ex) {
328: Mercurial.LOG.log(Level.INFO, null, ex);
329: }
330:
331: if (system == null) {
332: system = new Ini();
333: Mercurial.LOG.log(Level.WARNING, "Could not load the file "
334: + filePath + ". Falling back on hg defaults."); // NOI18N
335: }
336:
337: Ini global = null;
338: try {
339: global = new Ini(new FileReader(getGlobalConfigPath()
340: + File.separator + fileName)); // NOI18N
341: } catch (FileNotFoundException ex) {
342: // just doesn't exist - ignore
343: } catch (IOException ex) {
344: Mercurial.LOG.log(Level.INFO, null, ex);
345: }
346:
347: if (global != null) {
348: merge(global, system);
349: }
350: return system;
351: }
352:
353: /**
354: * Merges only sections/keys/values into target which are not already present in source
355: *
356: * @param source the source ini file
357: * @param target the target ini file in which the values from the source file are going to be merged
358: */
359: private void merge(Ini source, Ini target) {
360: for (Iterator<String> itSections = source.keySet().iterator(); itSections
361: .hasNext();) {
362: String sectionName = itSections.next();
363: Ini.Section sourceSection = source.get(sectionName);
364: Ini.Section targetSection = target.get(sectionName);
365:
366: if (targetSection == null) {
367: targetSection = target.add(sectionName);
368: }
369:
370: for (Iterator<String> itVariables = sourceSection.keySet()
371: .iterator(); itVariables.hasNext();) {
372: String key = itVariables.next();
373:
374: if (!targetSection.containsKey(key)) {
375: targetSection.put(key, sourceSection.get(key));
376: }
377: }
378: }
379: }
380:
381: /**
382: * Return the path for the systemwide command lines configuration directory
383: */
384: private static String getGlobalConfigPath() {
385: if (Utilities.isUnix()) {
386: return "/etc/mercurial"; // NOI18N
387: } else if (Utilities.isWindows()) {
388: return WINDOWS_GLOBAL_CONFIG_DIR;
389: }
390: return ""; // NOI18N
391: }
392:
393: /**
394: * Returns the value for the %APPDATA% env variable on windows
395: *
396: */
397: private static String getAPPDATA() {
398: String appdata = ""; // NOI18N
399: if (Utilities.isWindows()) {
400: appdata = System.getenv("APPDATA");// NOI18N
401: }
402: return appdata != null ? appdata : ""; // NOI18N
403: }
404:
405: /**
406: * Returns the value for the %ALLUSERSPROFILE% + the last foder segment from %APPDATA% env variables on windows
407: *
408: */
409: private static String getGlobalAPPDATA() {
410: if (Utilities.isWindows()) {
411: String globalProfile = System.getenv("ALLUSERSPROFILE"); // NOI18N
412: if (globalProfile == null
413: || globalProfile.trim().equals("")) { // NOI18N
414: globalProfile = ""; // NOI18N
415: }
416: String appdataPath = WINDOWS_USER_APPDATA;
417: if (appdataPath == null || appdataPath.equals("")) { // NOI18N
418: return ""; // NOI18N
419: }
420: String appdata = ""; // NOI18N
421: int idx = appdataPath.lastIndexOf("\\"); // NOI18N
422: if (idx > -1) {
423: appdata = appdataPath.substring(idx + 1);
424: if (appdata.trim().equals("")) { // NOI18N
425: int previdx = appdataPath.lastIndexOf("\\", idx); // NOI18N
426: if (idx > -1) {
427: appdata = appdataPath.substring(previdx + 1,
428: idx);
429: }
430: }
431: } else {
432: return ""; // NOI18N
433: }
434: return globalProfile + "/" + appdata; // NOI18N
435: }
436: return ""; // NOI18N
437: }
438: }
|