Skip to content
This repository has been archived by the owner on Jul 26, 2022. It is now read-only.

Coding Style

Philipp C. Heckel edited this page Aug 13, 2014 · 6 revisions

If you work in a group, it's important to follow a few shared concept and rules when it comes to coding style. We tried to assemble a few here. If in doubt, use the Eclipse auto format functionality using Ctrl+Shift+F.

Code conventions and formatting guidelines

  • Use tabs, not spaces
  • else/else if/try/catch/finally-statements should be in their own line
  • Before and after else/else if/try/catch/finally-statements, add an empty line
  • Before and after curly brackets ({ and }), add an empty line
  • Better more new lines than fewer new lines
  • Long lines are okay (150 chars)
  • Methods should be <= 20 lines
  • All if/for/while-statements need curly brackets (no one-liners), so instead of if (hasConflict) return;, write
    if (hasConflict) {\n
      return;\n
    }\n
    
  • Avoid ?:-syntax unless the statement is really easy
  • Avoid dependencies to other packages
  • Avoid using Singletons
  • Use understandable method/variable names, long method names are okay:
    public void go() { ... // Bad!
    public void findFirstConflictingDatabaseVersionHeader() { ... // Good!
    
    int dbvidx = ...; // Bad
    int databaseVersionHeaderIndex = ...; // Good
    
  • Avoid long boolean expressions in if-statements; use understandable variables instead:
    boolean existingAndWritableDirectory = ...;  
    if (existingAndWritableDirectory) { ...
    

JavaDoc and usage of TODO/FIXME markers

  • JavaDoc should exist for all classes and important methods
  • JavaDoc can contain HTML tags, lists should use <ul>/<ol>
  • JavaDoc {@link SomeClass} references should be used, but only if they do not need new imports
  • TODO/FIXME markers should be used with a relevance marker: TODO [low|medium|high]
    return "syncany://storage/1/" + ...; // TODO [low] Move syncany://-link to to constant
    

Example code

This code is taken from the InitOperation.

/*
 * Syncany, www.syncany.org
 * Copyright (C) 2011-2013 Philipp C. Heckel <philipp.heckel@gmail.com> 
 *
 * (...)
 */
package org.syncany.operations;

import java.io.ByteArrayInputStream;
// (...)

/**
 * The init operation initializes a new repository at a given remote storage
 * location. Its responsibilities include:
 * 
 * <ul>
 *   <li>Generating a master key from the user password (if encryption is enabled)
 *       using the {@link CipherUtil#createMasterKey(String) createMasterKey()} method</li>
 *   <li>Creating the local Syncany folder structure in the local directory (.syncany 
 *       folder and the sub-structure).</li>
 *   <li>Initializing the remote storage (creating folder-structure, if necessary)
 *       using the transfer manager's {@link TransferManager#init()} method.</li>
 *   <li>Creating a new repo and master file using {@link RepoTO} and {@link MasterTO},
 *       saving them locally and uploading them to the remote repository.</li>
 * </ul> 
 *   
 * @author Philipp C. Heckel <philipp.heckel@gmail.com>
 */
public class InitOperation extends AbstractInitOperation {
	private static final Logger logger = Logger.getLogger(InitOperation.class.getSimpleName());
	private InitOperationOptions options;
	private InitOperationListener listener;
	private TransferManager transferManager;

	public InitOperation(InitOperationOptions options, InitOperationListener listener) {
		super(null);

		this.options = options;
		this.listener = listener;
	}

	@Override
	public InitOperationResult execute() throws Exception {
		logger.log(Level.INFO, "");
		logger.log(Level.INFO, "Running 'Init'");
		logger.log(Level.INFO, "--------------------------------------------");

		transferManager = createTransferManager(options.getConfigTO().getConnectionTO());

		if (repoFileExistsOnRemoteStorage(transferManager)) {
			throw new Exception("Repo already exists. Use 'connect' command to connect to existing repository.");
		}

		// Create local .syncany directory
		File appDir = createAppDirs(options.getLocalDir());
		File configFile = new File(appDir + "/" + Config.FILE_CONFIG);
		File repoFile = new File(appDir + "/" + Config.FILE_REPO);
		File masterFile = new File(appDir + "/" + Config.FILE_MASTER);

		// Save config.xml and repo file

		String shareLink = null;
		boolean shareLinkEncrypted = false;

		if (options.isEncryptionEnabled()) {
			SaltedSecretKey masterKey = createMasterKeyFromPassword(options.getPassword()); // This takes looong!
			options.getConfigTO().setMasterKey(masterKey);

			writeXmlFile(new MasterTO(masterKey.getSalt()), masterFile);
			writeEncryptedXmlFile(options.getRepoTO(), repoFile, options.getCipherSpecs(), masterKey);

			shareLink = getEncryptedLink(options.getConfigTO().getConnectionTO(), options.getCipherSpecs(), masterKey);
			shareLinkEncrypted = true;
		}
		else {
			writeXmlFile(options.getRepoTO(), repoFile);

			shareLink = getPlaintextLink(options.getConfigTO().getConnectionTO());
			shareLinkEncrypted = false;
		}

		writeXmlFile(options.getConfigTO(), configFile);
		writeXmlFile(options.getRepoTO(), new File(repoFile + "-NOT-USED.xml")); // TODO [low] Remove this, not used

		// Make remote changes
		transferManager.init();

		if (masterFile.exists()) {
			uploadMasterFile(masterFile, transferManager);
		}

		uploadRepoFile(repoFile, transferManager);

		return new InitOperationResult(shareLink, shareLinkEncrypted);
	}

	private SaltedSecretKey createMasterKeyFromPassword(String masterPassword) throws Exception {
		if (listener != null) {
			listener.notifyGenerateMasterKey();
		}

		SaltedSecretKey masterKey = CipherUtil.createMasterKey(masterPassword);
		return masterKey;
	}

	protected boolean repoFileExistsOnRemoteStorage(TransferManager transferManager) throws Exception {
		try {
			Map<String, RepoRemoteFile> repoFileList = transferManager.list(RepoRemoteFile.class);

			if (repoFileList.containsKey("repo")) {
				return true;
			}
			else {
				return false;
			}
		}
		catch (Exception e) {
			throw new Exception("Unable to connect to repository.", e);
		}
	}

	private void uploadMasterFile(File masterFile, TransferManager transferManager) throws Exception {
		transferManager.upload(masterFile, new MasterRemoteFile());
	}

	private void uploadRepoFile(File repoFile, TransferManager transferManager) throws Exception {
		transferManager.upload(repoFile, new RepoRemoteFile());
	}

	private String getEncryptedLink(ConnectionTO connectionTO, List<CipherSpec> cipherSuites, SaltedSecretKey masterKey) throws Exception {
		ByteArrayOutputStream plaintextOutputStream = new ByteArrayOutputStream();
		Serializer serializer = new Persister();
		serializer.write(connectionTO, plaintextOutputStream);

		byte[] masterKeySalt = masterKey.getSalt();
		String masterKeySaltEncodedStr = new String(Base64.encodeBase64(masterKeySalt, false));

		byte[] encryptedConnectionBytes = CipherUtil.encrypt(new ByteArrayInputStream(plaintextOutputStream.toByteArray()), cipherSuites, masterKey);
		String encryptedEncodedStorageXml = new String(Base64.encodeBase64(encryptedConnectionBytes, false));

		return "syncany://storage/1/" + masterKeySaltEncodedStr + "-" + encryptedEncodedStorageXml; // TODO [low] Move to constant
	}

	private String getPlaintextLink(ConnectionTO connectionTO) throws Exception {
		ByteArrayOutputStream plaintextOutputStream = new ByteArrayOutputStream();
		Serializer serializer = new Persister();
		serializer.write(connectionTO, plaintextOutputStream);

		byte[] plaintextStorageXml = plaintextOutputStream.toByteArray();
		String plaintextEncodedStorageXml = new String(Base64.encodeBase64(plaintextStorageXml, false));

		return "syncany://storage/1/not-encrypted/" + plaintextEncodedStorageXml;  // TODO [low] Move to constant
	}

	public static interface InitOperationListener {
		public void notifyGenerateMasterKey();
	}

	public static class InitOperationOptions implements OperationOptions {
		private File localDir;
		private ConfigTO configTO;
		private RepoTO repoTO;
		private boolean encryptionEnabled;
		private List<CipherSpec> cipherSpecs;
		private String password;

		public File getLocalDir() {
			return localDir;
		}

		// (...)
	}

	public class InitOperationResult implements OperationResult {
		private String shareLink;
		private boolean shareLinkEncrypted;

		// (...)
	}
}