You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Identity.java 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. /*
  2. * Copyright (c) 2006-2011 Chris Smith, Shane Mc Cormack, Gregory Holmes
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.config;
  23. import com.dmdirc.Main;
  24. import com.dmdirc.interfaces.ConfigChangeListener;
  25. import com.dmdirc.logger.ErrorLevel;
  26. import com.dmdirc.logger.Logger;
  27. import com.dmdirc.util.ConfigFile;
  28. import com.dmdirc.util.InvalidConfigFileException;
  29. import com.dmdirc.util.WeakList;
  30. import com.dmdirc.util.validators.Validator;
  31. import java.io.File;
  32. import java.io.IOException;
  33. import java.io.InputStream;
  34. import java.util.ArrayList;
  35. import java.util.HashMap;
  36. import java.util.HashSet;
  37. import java.util.LinkedList;
  38. import java.util.List;
  39. import java.util.Map;
  40. import java.util.Set;
  41. import java.util.logging.Level;
  42. /**
  43. * An identity is a group of settings that are applied to a connection, server,
  44. * network or channel. Identities may be automatically applied in certain
  45. * cases, or the user may manually apply them.
  46. * <p>
  47. * Note: this class has a natural ordering that is inconsistent with equals.
  48. */
  49. public class Identity extends ConfigSource implements Comparable<Identity> {
  50. /** A regular expression that will match all characters illegal in file names. */
  51. protected static final String ILLEGAL_CHARS = "[\\\\\"/:\\*\\?\"<>\\|]";
  52. /** The domain used for identity settings. */
  53. private static final String DOMAIN = "identity".intern();
  54. /** The domain used for profile settings. */
  55. private static final String PROFILE_DOMAIN = "profile".intern();
  56. /** A logger for this class. */
  57. private static final java.util.logging.Logger LOGGER = java.util.logging
  58. .Logger.getLogger(Identity.class.getName());
  59. /** The target for this identity. */
  60. protected final ConfigTarget myTarget;
  61. /** The configuration details for this identity. */
  62. protected final ConfigFile file;
  63. /** The global config manager. */
  64. protected ConfigManager globalConfig;
  65. /** The config change listeners for this source. */
  66. protected final List<ConfigChangeListener> listeners
  67. = new WeakList<ConfigChangeListener>();
  68. /** Whether this identity needs to be saved. */
  69. protected boolean needSave;
  70. /**
  71. * Creates a new instance of Identity.
  72. *
  73. * @param file The file to load this identity from
  74. * @param forceDefault Whether to force this identity to be loaded as default
  75. * identity or not
  76. * @throws InvalidIdentityFileException Missing required properties
  77. * @throws IOException Input/output exception
  78. */
  79. public Identity(final File file, final boolean forceDefault) throws IOException,
  80. InvalidIdentityFileException {
  81. super();
  82. this.file = new ConfigFile(file);
  83. this.file.setAutomake(true);
  84. initFile(forceDefault);
  85. myTarget = getTarget(forceDefault);
  86. if (myTarget.isCustom(PROFILE_DOMAIN)) {
  87. migrateProfile();
  88. }
  89. }
  90. /**
  91. * Creates a new read-only identity.
  92. *
  93. * @param stream The input stream to read the identity from
  94. * @param forceDefault Whether to force this identity to be loaded as default
  95. * identity or not
  96. * @throws InvalidIdentityFileException Missing required properties
  97. * @throws IOException Input/output exception
  98. */
  99. public Identity(final InputStream stream, final boolean forceDefault) throws IOException,
  100. InvalidIdentityFileException {
  101. super();
  102. this.file = new ConfigFile(stream);
  103. this.file.setAutomake(true);
  104. initFile(forceDefault);
  105. myTarget = getTarget(forceDefault);
  106. if (myTarget.isCustom(PROFILE_DOMAIN)) {
  107. migrateProfile();
  108. }
  109. }
  110. /**
  111. * Creates a new identity from the specified config file.
  112. *
  113. * @param configFile The config file to use
  114. * @param target The target of this identity
  115. */
  116. public Identity(final ConfigFile configFile, final ConfigTarget target) {
  117. super();
  118. this.file = configFile;
  119. this.file.setAutomake(true);
  120. this.myTarget = target;
  121. if (myTarget.isCustom(PROFILE_DOMAIN)) {
  122. migrateProfile();
  123. }
  124. }
  125. /**
  126. * Determines and returns the target for this identity from its contents.
  127. *
  128. * @param forceDefault Whether to force this to be a default identity
  129. * @return A ConfigTarget for this identity
  130. * @throws InvalidIdentityFileException If the identity isn't valid
  131. */
  132. private ConfigTarget getTarget(final boolean forceDefault)
  133. throws InvalidIdentityFileException {
  134. final ConfigTarget target = new ConfigTarget();
  135. if (hasOptionString(DOMAIN, "ircd")) {
  136. target.setIrcd(getOption(DOMAIN, "ircd"));
  137. } else if (hasOptionString(DOMAIN, "protocol")) {
  138. target.setProtocol(getOption(DOMAIN, "protocol"));
  139. } else if (hasOptionString(DOMAIN, "network")) {
  140. target.setNetwork(getOption(DOMAIN, "network"));
  141. } else if (hasOptionString(DOMAIN, "server")) {
  142. target.setServer(getOption(DOMAIN, "server"));
  143. } else if (hasOptionString(DOMAIN, "channel")) {
  144. target.setChannel(getOption(DOMAIN, "channel"));
  145. } else if (hasOptionString(DOMAIN, "globaldefault")) {
  146. target.setGlobalDefault();
  147. } else if (hasOptionString(DOMAIN, "global") || (forceDefault && !isProfile())) {
  148. target.setGlobal();
  149. } else if (isProfile()) {
  150. target.setCustom(PROFILE_DOMAIN);
  151. } else if (hasOptionString(DOMAIN, "type")) {
  152. target.setCustom(getOption(DOMAIN, "type"));
  153. } else {
  154. throw new InvalidIdentityFileException("No target and no profile");
  155. }
  156. if (hasOptionString(DOMAIN, "order")) {
  157. target.setOrder(getOptionInt(DOMAIN, "order"));
  158. }
  159. return target;
  160. }
  161. /**
  162. * Initialises this identity from a file.
  163. *
  164. * @since 0.6.3
  165. * @param forceDefault Whether to force this to be a default identity
  166. * @param file The file to load this identity from (or null)
  167. * @throws InvalidIdentityFileException if the identity file is invalid
  168. * @throws IOException On I/O exception when reading the identity
  169. */
  170. private void initFile(final boolean forceDefault)
  171. throws InvalidIdentityFileException, IOException {
  172. try {
  173. this.file.read();
  174. } catch (InvalidConfigFileException ex) {
  175. throw new InvalidIdentityFileException(ex);
  176. }
  177. if (!hasOptionString(DOMAIN, "name") && !forceDefault) {
  178. throw new InvalidIdentityFileException("No name specified");
  179. }
  180. }
  181. /**
  182. * Attempts to reload this identity from disk. If this identity has been
  183. * modified (i.e., {@code needSave} is true), then this method silently
  184. * returns straight away. All relevant ConfigChangeListeners are fired for
  185. * new, altered and deleted properties. The target of the identity will not
  186. * be changed by this method, even if it has changed on disk.
  187. *
  188. * @throws java.io.IOException On I/O exception when reading the identity
  189. * @throws InvalidConfigFileException if the config file is no longer valid
  190. */
  191. public void reload() throws IOException, InvalidConfigFileException {
  192. if (needSave) {
  193. return;
  194. }
  195. final List<String[]> changes = new LinkedList<String[]>();
  196. synchronized (this) {
  197. final Map<String, Map<String, String>> oldProps = file.getKeyDomains();
  198. file.read();
  199. for (Map.Entry<String, Map<String, String>> entry : file.getKeyDomains().entrySet()) {
  200. final String domain = entry.getKey();
  201. for (Map.Entry<String, String> subentry : entry.getValue().entrySet()) {
  202. final String key = subentry.getKey();
  203. final String value = subentry.getValue();
  204. if (!oldProps.containsKey(domain)) {
  205. changes.add(new String[]{domain, key});
  206. } else if (!oldProps.get(domain).containsKey(key)
  207. || !oldProps.get(domain).get(key).equals(value)) {
  208. changes.add(new String[]{domain, key});
  209. oldProps.get(domain).remove(key);
  210. }
  211. }
  212. if (oldProps.containsKey(domain)) {
  213. for (String key : oldProps.get(domain).keySet()) {
  214. changes.add(new String[]{domain, key});
  215. }
  216. }
  217. }
  218. }
  219. for (String[] change : changes) {
  220. fireSettingChange(change[0], change[1]);
  221. }
  222. }
  223. /**
  224. * Fires the config changed listener for the specified option after this
  225. * identity is reloaded.
  226. *
  227. * @param domain The domain of the option that's changed
  228. * @param key The key of the option that's changed
  229. * @since 0.6.3m1
  230. */
  231. private void fireSettingChange(final String domain, final String key) {
  232. for (ConfigChangeListener listener : new ArrayList<ConfigChangeListener>(listeners)) {
  233. listener.configChanged(domain, key);
  234. }
  235. }
  236. /**
  237. * Returns the name of this identity.
  238. *
  239. * @return The name of this identity
  240. */
  241. public String getName() {
  242. if (hasOptionString(DOMAIN, "name")) {
  243. return getOption(DOMAIN, "name");
  244. } else {
  245. return "Unnamed";
  246. }
  247. }
  248. /**
  249. * Checks if this profile needs migrating from the old method of
  250. * storing nicknames (profile.nickname + profile.altnicks) to the new
  251. * method (profile.nicknames), and performs the migration if needed.
  252. *
  253. * @since 0.6.3m1
  254. */
  255. protected void migrateProfile() {
  256. if (hasOptionString(PROFILE_DOMAIN, "nickname")) {
  257. // Migrate
  258. setOption(PROFILE_DOMAIN, "nicknames", getOption(PROFILE_DOMAIN, "nickname")
  259. + (hasOptionString(PROFILE_DOMAIN, "altnicks") ? "\n"
  260. + getOption(PROFILE_DOMAIN, "altnicks") : ""));
  261. unsetOption(PROFILE_DOMAIN, "nickname");
  262. unsetOption(PROFILE_DOMAIN, "altnicks");
  263. }
  264. }
  265. /**
  266. * Determines whether this identity can be used as a profile when
  267. * connecting to a server. Profiles are identities that can supply
  268. * nick, ident, real name, etc.
  269. *
  270. * @return True iff this identity can be used as a profile
  271. */
  272. public boolean isProfile() {
  273. return (hasOptionString(PROFILE_DOMAIN, "nicknames")
  274. || hasOptionString(PROFILE_DOMAIN, "nickname"))
  275. && hasOptionString(PROFILE_DOMAIN, "realname");
  276. }
  277. /** {@inheritDoc} */
  278. @Override
  279. protected boolean hasOption(final String domain, final String option,
  280. final Validator<String> validator) {
  281. return file.isKeyDomain(domain)
  282. && file.getKeyDomain(domain).containsKey(option)
  283. && !validator.validate(file.getKeyDomain(domain).get(option)).isFailure();
  284. }
  285. /** {@inheritDoc} */
  286. @Override
  287. public synchronized String getOption(final String domain,
  288. final String option, final Validator<String> validator) {
  289. final String value = file.getKeyDomain(domain).get(option);
  290. if (validator.validate(value).isFailure()) {
  291. return null;
  292. }
  293. return value;
  294. }
  295. /**
  296. * Sets the specified option in this identity to the specified value.
  297. *
  298. * @param domain The domain of the option
  299. * @param option The name of the option
  300. * @param value The new value for the option
  301. */
  302. public void setOption(final String domain, final String option,
  303. final String value) {
  304. String oldValue;
  305. boolean unset = false;
  306. synchronized (this) {
  307. oldValue = getOption(domain, option);
  308. LOGGER.finest(getName() + ": setting " + domain + "." + option + " to " + value);
  309. if (myTarget.getType() == ConfigTarget.TYPE.GLOBAL) {
  310. // If we're the global config, don't set useless settings that are
  311. // covered by global defaults.
  312. if (globalConfig == null) {
  313. globalConfig = new ConfigManager("", "", "", "");
  314. }
  315. globalConfig.removeIdentity(this);
  316. if (globalConfig.hasOptionString(domain, option)
  317. && globalConfig.getOption(domain, option).equals(value)) {
  318. // The new value is covered by a default setting
  319. if (oldValue == null) {
  320. // There was no old value, so we don't need to do anything
  321. return;
  322. } else {
  323. // There was an old value, so we need to unset it so
  324. // that the default shows through.
  325. file.getKeyDomain(domain).remove(option);
  326. needSave = true;
  327. unset = true;
  328. }
  329. }
  330. }
  331. if (!unset && ((oldValue == null && value != null)
  332. || (oldValue != null && !oldValue.equals(value)))) {
  333. file.getKeyDomain(domain).put(option, value);
  334. needSave = true;
  335. }
  336. }
  337. // Fire any setting change listeners now we're no longer holding
  338. // a lock on this identity.
  339. if (unset || (oldValue == null && value != null)
  340. || (oldValue != null && !oldValue.equals(value))) {
  341. fireSettingChange(domain, option);
  342. }
  343. }
  344. /**
  345. * Sets the specified option in this identity to the specified value.
  346. *
  347. * @param domain The domain of the option
  348. * @param option The name of the option
  349. * @param value The new value for the option
  350. */
  351. public void setOption(final String domain, final String option,
  352. final int value) {
  353. setOption(domain, option, String.valueOf(value));
  354. }
  355. /**
  356. * Sets the specified option in this identity to the specified value.
  357. *
  358. * @param domain The domain of the option
  359. * @param option The name of the option
  360. * @param value The new value for the option
  361. */
  362. public void setOption(final String domain, final String option,
  363. final boolean value) {
  364. setOption(domain, option, String.valueOf(value));
  365. }
  366. /**
  367. * Sets the specified option in this identity to the specified value.
  368. *
  369. * @param domain The domain of the option
  370. * @param option The name of the option
  371. * @param value The new value for the option
  372. */
  373. public void setOption(final String domain, final String option,
  374. final List<String> value) {
  375. final StringBuilder temp = new StringBuilder();
  376. for (String part : value) {
  377. temp.append('\n');
  378. temp.append(part);
  379. }
  380. setOption(domain, option, temp.length() > 0 ? temp.substring(1) : temp.toString());
  381. }
  382. /**
  383. * Unsets a specified option.
  384. *
  385. * @param domain domain of the option
  386. * @param option name of the option
  387. */
  388. public void unsetOption(final String domain, final String option) {
  389. synchronized (this) {
  390. file.getKeyDomain(domain).remove(option);
  391. needSave = true;
  392. }
  393. fireSettingChange(domain, option);
  394. }
  395. /**
  396. * Returns the set of domains available in this identity.
  397. *
  398. * @since 0.6
  399. * @return The set of domains used by this identity
  400. */
  401. public Set<String> getDomains() {
  402. return new HashSet<String>(file.getKeyDomains().keySet());
  403. }
  404. /**
  405. * Retrieves a map of all options within the specified domain in this
  406. * identity.
  407. *
  408. * @param domain The domain to retrieve
  409. * @since 0.6
  410. * @return A map of option names to values
  411. */
  412. public synchronized Map<String, String> getOptions(final String domain) {
  413. return new HashMap<String, String>(file.getKeyDomain(domain));
  414. }
  415. /**
  416. * Saves this identity to disk if it has been updated.
  417. */
  418. public synchronized void save() {
  419. LOGGER.log(Level.FINE, "{0}: save(); needsave = {1}", new Object[]{getName(), needSave});
  420. if (needSave && file != null && file.isWritable()) {
  421. if (myTarget != null && myTarget.getType() == ConfigTarget.TYPE.GLOBAL) {
  422. LOGGER.log(Level.FINER, "{0}: I''m a global config", getName());
  423. // This branch is executed if this identity is global. In this
  424. // case, we build a global config (removing ourself and the
  425. // versions identity) and compare our values to the values
  426. // contained in that. Any values that are the same can be unset
  427. // from this identity (as they will default to their current
  428. // value).
  429. //
  430. // Note that the updater channel is included in the version
  431. // identity, and this is excluded from the global config. This
  432. // means that once you manually set the channel it will stay
  433. // like that until you manually change it again, as opposed
  434. // to being removed as soon as you use a build from that
  435. // channel.
  436. if (globalConfig == null) {
  437. globalConfig = new ConfigManager("", "", "", "");
  438. }
  439. globalConfig.removeIdentity(this);
  440. globalConfig.removeIdentity(IdentityManager.getVersionIdentity());
  441. if (LOGGER.isLoggable(Level.FINEST)) {
  442. for (Identity source : globalConfig.getSources()) {
  443. LOGGER.log(Level.FINEST, "{0}: source: {1}",
  444. new Object[]{getName(), source.getName()});
  445. }
  446. }
  447. for (Map.Entry<String, Map<String, String>> entry
  448. : file.getKeyDomains().entrySet()) {
  449. final String domain = entry.getKey();
  450. for (Map.Entry<String, String> subentry
  451. : new HashSet<Map.Entry<String, String>>(entry.getValue().entrySet())) {
  452. final String key = subentry.getKey();
  453. final String value = subentry.getValue();
  454. if (globalConfig.hasOptionString(domain, key)
  455. && globalConfig.getOption(domain, key).equals(value)) {
  456. LOGGER.log(Level.FINEST,
  457. "{0}: found superfluous setting: {1}.{2} (= {3})",
  458. new Object[]{getName(), domain, key, value});
  459. file.getKeyDomain(domain).remove(key);
  460. }
  461. }
  462. }
  463. }
  464. if (file.isKeyDomain("temp")) {
  465. file.getKeyDomain("temp").clear();
  466. }
  467. try {
  468. file.write();
  469. needSave = false;
  470. } catch (IOException ex) {
  471. Logger.userError(ErrorLevel.MEDIUM,
  472. "Unable to save identity file: " + ex.getMessage());
  473. }
  474. }
  475. }
  476. /**
  477. * Deletes this identity from disk.
  478. */
  479. public synchronized void delete() {
  480. if (file != null) {
  481. file.delete();
  482. }
  483. IdentityManager.removeIdentity(this);
  484. }
  485. /**
  486. * Retrieves this identity's target.
  487. *
  488. * @return The target of this identity
  489. */
  490. public ConfigTarget getTarget() {
  491. return myTarget;
  492. }
  493. /**
  494. * Determines if this identity is loaded from the specified File.
  495. *
  496. * @param target The file to be checked
  497. * @return True if this identity comes from the target file, false otherwise
  498. * @since 0.6.4
  499. */
  500. public boolean isFile(final File target) {
  501. return file != null && file.getFile() != null && file.getFile().equals(target);
  502. }
  503. /**
  504. * Adds a new config change listener for this identity.
  505. *
  506. * @param listener The listener to be added
  507. */
  508. public void addListener(final ConfigChangeListener listener) {
  509. listeners.add(listener);
  510. }
  511. /**
  512. * Removes the specific config change listener from this identity.
  513. *
  514. * @param listener The listener to be removed
  515. */
  516. public void removeListener(final ConfigChangeListener listener) {
  517. listeners.remove(listener);
  518. }
  519. /**
  520. * Returns a string representation of this object (its name).
  521. *
  522. * @return A string representation of this object
  523. */
  524. @Override
  525. public String toString() {
  526. return getName();
  527. }
  528. /** {@inheritDoc} */
  529. @Override
  530. public int hashCode() {
  531. return getName().hashCode() + getTarget().hashCode();
  532. }
  533. /** {@inheritDoc} */
  534. @Override
  535. public boolean equals(final Object obj) {
  536. return obj instanceof Identity
  537. && getName().equals(((Identity) obj).getName())
  538. && getTarget() == ((Identity) obj).getTarget();
  539. }
  540. /**
  541. * Compares this identity to another config source to determine which
  542. * is more specific.
  543. *
  544. * @param target The Identity to compare to
  545. * @return -1 if this config source is less specific, 0 if they're equal,
  546. * +1 if this config is more specific.
  547. */
  548. @Override
  549. public int compareTo(final Identity target) {
  550. return target.getTarget().compareTo(myTarget);
  551. }
  552. /**
  553. * Creates a new identity containing the specified properties.
  554. *
  555. * @param settings The settings to populate the identity with
  556. * @return A new identity containing the specified properties
  557. * @throws IOException If the file cannot be created
  558. * @throws InvalidIdentityFileException If the settings are invalid
  559. * @since 0.6.3m1
  560. */
  561. protected static Identity createIdentity(final Map<String, Map<String, String>> settings)
  562. throws IOException, InvalidIdentityFileException {
  563. if (!settings.containsKey(DOMAIN) || !settings.get(DOMAIN).containsKey("name")
  564. || settings.get(DOMAIN).get("name").isEmpty()) {
  565. throw new InvalidIdentityFileException("identity.name is not set");
  566. }
  567. final String fs = System.getProperty("file.separator");
  568. final String location = Main.getConfigDir() + "identities" + fs;
  569. final String name = settings.get(DOMAIN).get("name").replaceAll(ILLEGAL_CHARS, "_");
  570. File file = new File(location + name);
  571. int attempt = 0;
  572. while (file.exists()) {
  573. file = new File(location + name + "-" + ++attempt);
  574. }
  575. final ConfigFile configFile = new ConfigFile(file);
  576. for (Map.Entry<String, Map<String, String>> entry : settings.entrySet()) {
  577. configFile.addDomain(entry.getKey(), entry.getValue());
  578. }
  579. configFile.write();
  580. final Identity identity = new Identity(file, false);
  581. IdentityManager.addIdentity(identity);
  582. return identity;
  583. }
  584. /**
  585. * Generates an empty identity for the specified target.
  586. *
  587. * @param target The target for the new identity
  588. * @return An empty identity for the specified target
  589. * @throws IOException if the file can't be written
  590. * @see #createIdentity(java.util.Map)
  591. */
  592. public static Identity buildIdentity(final ConfigTarget target)
  593. throws IOException {
  594. final Map<String, Map<String, String>> settings
  595. = new HashMap<String, Map<String, String>>();
  596. settings.put(DOMAIN, new HashMap<String, String>(2));
  597. settings.get(DOMAIN).put("name", target.getData());
  598. settings.get(DOMAIN).put(target.getTypeName(), target.getData());
  599. try {
  600. return createIdentity(settings);
  601. } catch (InvalidIdentityFileException ex) {
  602. Logger.appError(ErrorLevel.MEDIUM, "Unable to create identity", ex);
  603. return null;
  604. }
  605. }
  606. /**
  607. * Generates an empty profile with the specified name. Note the name is used
  608. * as a file name, so should be sanitised.
  609. *
  610. * @param name The name of the profile to create
  611. * @return A new profile with the specified name
  612. * @throws IOException If the file can't be written
  613. * @see #createIdentity(java.util.Map)
  614. */
  615. public static Identity buildProfile(final String name) throws IOException {
  616. final Map<String, Map<String, String>> settings
  617. = new HashMap<String, Map<String, String>>();
  618. settings.put(DOMAIN, new HashMap<String, String>(1));
  619. settings.put(PROFILE_DOMAIN, new HashMap<String, String>(2));
  620. final String nick = System.getProperty("user.name").replace(' ', '_');
  621. settings.get(DOMAIN).put("name", name);
  622. settings.get(PROFILE_DOMAIN).put("nicknames", nick);
  623. settings.get(PROFILE_DOMAIN).put("realname", nick);
  624. try {
  625. return createIdentity(settings);
  626. } catch (InvalidIdentityFileException ex) {
  627. Logger.appError(ErrorLevel.MEDIUM, "Unable to create identity", ex);
  628. return null;
  629. }
  630. }
  631. /**
  632. * Generates a custom identity with the specified name and type. Note the
  633. * name is used as a file name, so should be sanitised.
  634. *
  635. * @param type The type of the identity to create
  636. * @param name The name of the identity to create
  637. * @return A new custom identity with the specified name
  638. * @throws IOException If the file can't be written
  639. */
  640. public static Identity buildIdentity(final String name, final String type)
  641. throws IOException {
  642. final Map<String, Map<String, String>> settings
  643. = new HashMap<String, Map<String, String>>();
  644. settings.put(DOMAIN, new HashMap<String, String>(2));
  645. settings.get(DOMAIN).put("name", name);
  646. settings.get(DOMAIN).put("type", type);
  647. try {
  648. return createIdentity(settings);
  649. } catch (InvalidIdentityFileException ex) {
  650. Logger.appError(ErrorLevel.MEDIUM, "Unable to create identity", ex);
  651. return null;
  652. }
  653. }
  654. }