Skip to content

Instantly share code, notes, and snippets.

@fracz
Last active July 18, 2017 20:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fracz/14f22f67333195a936a6f920b2dc47ec to your computer and use it in GitHub Desktop.
Save fracz/14f22f67333195a936a6f920b2dc47ec to your computer and use it in GitHub Desktop.
Experimental Validation of Source Code Reviews on Mobile Devices: task sources.

Experimental Validation of Source Code Reviews on Mobile Devices

This gist presents the source code of the Task 1 (Line.java) and Task 2 (the rest of the files) that has been used to verify mobile source code reviews effectiveness.

The experiment results are described in the Experimental Validation of Source Code Reviews on Mobile Devices.

Code smells in Line.java

No. Line numbers Remarks
1.1 3-7 Useless comment with code history, such information should be stored in VCS
1.2 24-26, 28 Javadoc comment is a make up for a non-exhaustive class name
1.3 28-117 SRP violation - presentation code should be splitted from the business logic
1.4 27 Add constructor instead of warning suppression or add a comment explaining why is it really mandatory
1.5 30, 115 TWO constant has no logical meaning
1.6 32, 34 Useless encoding of variable names with _ prefix
1.7 36 Useless / lying / obsolete comment
1.8 39 The name of the field should be lineContentView instead of lineContent to be consistend with others
1.9 41, SourceFile:20 SourceFile class is not Serializable but is used as a field in serializable Line class
1.10 43 Field comments is not initialized anywhere
1.11 43 Line should refer to AbstractComment instead of Comment
1.12 45-46, SourceFile:67 Too many constructor arguments, use builder instead
1.13 45-61 Constructor is too long and it does more than one thing
1.14 46, 100 Flag (boolean) argument should be replaced by another method
1.15 53, 59 Calling an overridable method from a constructor instead of using context argument
1.16 54 Syntax error (extra ;)
1.17 56 Magic value - a number
1.18 59 Semantic error: value is stored in local variable instead of class field. As a result, a NullPointerException would be thrown when the class is instantiated
1.19 59 - 60 Hidden coupling: method execution from lin 60 relies on initialization from line 59, but it is not ensured by its arguments
1.20 60, 109 Vertical distance: method is declared far away from its usage
1.21 63, Comment:13 Ambiguous method name - should be getLineNumber() instead of simply get()
1.22 67-72, 82-87 Useless comment
1.23 73, 88 Inconsistency of method names that perform similar tasks (addTextComment and createVoiceComment)
1.24 75-79, 90-94 Code duplication
1.25 77, 92 Reinventing the wheel - use isEmpty() method from java.util.List instead of comparing list size to zero
1.26 77, 92 if is useless, comments list is never empty here as previous line adds one
1.27 78, 93 Magic value - string
1.28 78, 93 Feature envy: highlighting line with should be separated to class responsible for appearance to make code more readable
1.29 88 Typographic error in argument name - recodedFile instead of recordedFile
1.30 97 - 98 Commented out, not used source code
1.31 100, 111 Inconsistent code formatting: lack of space before opening curly brace
1.32 101 Overcomplicated, negative boolean expression
1.33 101 - 104 Lack of curly braces around if...else blocks
1.34 102, 104 Code duplication; result of Html.fromHtml(lineOfCode) might be saved to explanation variable
1.35 102, 104 lineOfCode is used but _lineOfCode is declared
1.36 110 Method should return defensive copy of a list
1.37 113 - 116 Method has side effects: it is doing more than its name suggests

Code smells in SourceFile.java

No. Line numbers Remarks
2.1 23, 27, 30, 92 Feature envy and misplaced responsibility: line should be able to highlight itself with encapsulated colors
2.2 25 - 31 Code might be considered too complicated for anonymous initialization, introduce inner class
2.3 43 Constructor should be private when static factory methods are provided
2.4 49 Obscured intent: changing implementation of this method would change the method name, encapsulation violated
2.5 44, 52, 95 Hidden temporal coupling on sourceCode field
2.6 53 StringBuilder should be used instead of StringBuffer, thread safety is not required here
2.7 54 - 56 Too low level of abstraction for the method: some operations should be extracted to meaningful method or even a dependency
2.8 54 Foreach loop could be used
2.9 59 AssertionError should not hide exception
2.10 64 - 70 sourceCode is final, thus, lines collection could be build once per SourceFile
2.11 65 Unnecessary diamonds value
2.12 67 - 69 Wrong level of abstraction - different method
2.13 74 - 78 Non thread-safe lazy initialization
2.14 91 - 94 Tab replacement could be build once per source file or even per application instance
2.15 103 - 105, 110-112 Code duplication
2.16 103 - 106, 110 - 113 Both null check and method call should use the same field access or getter access, not mix them
2.17 120 - 122 pointless code
2.18 128 - 130 Javadoc comment is a makeup for poor static factory method name
2.19 133 Explaining variables needed

Code smells in AbstractComment.java

No. Line numbers Remarks
2.20 9 Abstract class declared as not abstract
2.21 10 Date format should be static as it does not change in instances
2.22 16 Unused field
2.23 18, 20 date, lineNumber should be final as they do not have setters
2.24 24, 26 Not testable initializations
2.25 26 Redundant parameter - default constructor behaves in the same way as new Date(System.currentTimeMillis())
2.26 37-39 Method should provide synchronization as SimpleDateFormat is not thread safe

Code smells in Comment.java

No. Line numbers Remarks
2.27 the whole file This class should be divided into two - TextComment and VoiceComment
2.28 12, Line:74, 89 Class does not need Line object, passing an int would be a better idea
package pl.fracz.mcr.comment;
import pl.fracz.mcr.preferences.ApplicationSettings;
import pl.fracz.mcr.source.SourceFile;
import java.text.SimpleDateFormat;
import java.util.Date;
public class AbstractComment {
private final SimpleDateFormat CREATION_TIME_FORMAT = new SimpleDateFormat("HH:mm dd.MM.yyyy");
private final Type type;
private final String author;
protected SourceFile sourceFile;
private Date date;
private int lineNumber;
public AbstractComment(Type type, int lineNumber) {
this.type = type;
this.author = ApplicationSettings.getAuthor();
this.lineNumber = lineNumber;
this.date = new Date(System.currentTimeMillis());
}
public Type getType() {
return type;
}
public Date getDate() {
return date;
}
public String getDateFormatted() {
return CREATION_TIME_FORMAT.format(date);
}
public int getLineNumber() {
return lineNumber;
}
protected String getAuthor() {
return author;
}
public static enum Type {
TEXT, VOICE
}
}
package pl.fracz.mcr.comment;
import pl.fracz.mcr.source.Line;
import java.io.File;
public class Comment extends AbstractComment {
private String text;
private File file;
public Comment(AbstractComment.Type type, Line line) {
super(type, line.get());
}
public void setText(String text) {
checkValidType(AbstractComment.Type.TEXT);
this.text = text;
}
public String getText() {
checkValidType(AbstractComment.Type.TEXT);
return text;
}
public void setFile(File file) {
checkValidType(AbstractComment.Type.VOICE);
this.file = file;
}
public File getFile() {
checkValidType(AbstractComment.Type.VOICE);
return file;
}
private void checkValidType(AbstractComment.Type type) {
if (type != this.getType()) {
throw new IllegalArgumentException("The " + type + " comment is required to set this attribute");
}
}
}
package pl.fracz.mcr.comment;
public class CommentNotAddedException extends Exception {
}
package pl.fracz.mcr.source;
/*
2013-10-23, fracz, first implementation
2013-10-30, fracz, added syntax highlighting
2014-02-26, fracz, added ability to add voice comment
*/
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.text.Html;
import android.widget.LinearLayout;
import android.widget.TextView;
import pl.fracz.mcr.comment.AbstractComment;
import pl.fracz.mcr.comment.Comment;
import pl.fracz.mcr.comment.CommentNotAddedException;
import java.io.File;
import java.io.Serializable;
import java.util.List;
/**
* View that represents one line of code.
*/
@SuppressLint("ViewConstructor")
public class Line extends LinearLayout implements Serializable {
private static final long serialVersionUID = 3076583280108678995L;
private static final int TWO = 2;
private final int _lineNumber;
private final String _lineOfCode;
// holds the line number
private TextView lineNumberView;
private TextView lineContent;
private SourceFile sourceFile;
private List<Comment> comments;
public Line(Context context, SourceFile sourceFile, int lineNumber,
String lineOfCode, boolean syntaxColor) {
super(context);
this.sourceFile = sourceFile;
this._lineNumber = lineNumber;
this._lineOfCode = lineOfCode;
setOrientation(LinearLayout.HORIZONTAL);
lineNumberView = new TextView(getContext());
lineNumberView.setText(String.format("%d.", lineNumber););
lineNumberView.setSingleLine();
lineNumberView.setWidth(30);
addView(lineNumberView);
TextView lineContent = new TextView(getContext());
addLineContent(syntaxColor);
}
public int get() {
return _lineNumber;
}
/**
* Adds a text comment.
*
* @param comment
* @throws CommentNotAddedException
*/
public void addTextComment(String comment) throws CommentNotAddedException {
Comment textComment = new Comment(AbstractComment.Type.TEXT, this);
textComment.setText(comment);
comments.add(textComment);
if (comments.size() > 0) {
lineNumberView.setBackgroundColor(Color.parseColor("#008000"));
}
}
/**
* Adds a voice comment.
*
* @param recordedFile
* @throws CommentNotAddedException
*/
public void createVoiceComment(File recodedFile) throws CommentNotAddedException {
Comment voiceComment = new Comment(AbstractComment.Type.VOICE, this);
voiceComment.setFile(recodedFile);
comments.add(voiceComment);
if (comments.size() > 0) {
lineNumberView.setBackgroundColor(Color.parseColor("#008000"));
}
}
// public void addVideoComment(File videoFile) throws CommentNotAddedException {
// }
private void addLineContent(boolean syntaxColor){
if (!syntaxColor || !SyntaxHighlighter.canBeHighlighted(syntaxColor))
lineContent.setText(Html.fromHtml(lineOfCode));
else
lineContent.setText(SyntaxHighlighter.highlight(Html.fromHtml(lineOfCode)));
lineContent.setTypeface(Typeface.MONOSPACE);
addView(lineContent);
}
public List<Comment> getComments(){
return this.comments;
}
public boolean hasConversation() {
sourceFile.markConversation(this);
return getComments().size() > TWO;
}
}
package pl.fracz.mcr.source;
import pl.fracz.mcr.comment.CommentNotAddedException;
public class NoSelectedLineException extends CommentNotAddedException {
}
package pl.fracz.mcr.source;
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import pl.fracz.mcr.comment.CommentNotAddedException;
import pl.fracz.mcr.preferences.ApplicationSettings;
import pl.fracz.mcr.syntax.PrettifyHighlighter;
import pl.fracz.mcr.syntax.SyntaxHighlighter;
import pl.fracz.mcr.util.FileUtils;
import java.io.File;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.StringTokenizer;
public class SourceFile {
private static final SyntaxHighlighter SYNTAX_HIGHLIGHTER = new PrettifyHighlighter();
private static final Color SELECTED_LINE_COLOR = Color.parseColor("#444444");
private final View.OnClickListener lineHighlighter = view -> {
if (selectedLine != null) {
selectedLine.setBackgroundColor(Color.TRANSPARENT);
}
selectedLine = (Line) view;
selectedLine.setBackgroundColor(SELECTED_LINE_COLOR);
};
private final String sourceCode;
private final String identifier;
private final String language;
private Line selectedLine;
private String highlightedSourceCode;
public SourceFile(String sourceCode, String language) {
this.sourceCode = sourceCode;
this.language = language;
this.identifier = calculateSHA1SourceChecksum();
}
private String calculateSHA1SourceChecksum() {
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] digest = md.digest(sourceCode.getBytes());
StringBuffer sb = new StringBuffer();
for (int i = 0; i < digest.length; i++) {
sb.append(Integer.toString((digest[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
throw new AssertionError();
}
}
public Collection<Line> getLines(Context context) {
StringTokenizer tokenizer = new StringTokenizer(getHighlightedSourceCode(), "\n");
Collection<Line> lines = new ArrayList<Line>(tokenizer.countTokens());
while (tokenizer.hasMoreTokens()) {
Line line = new Line(context, this, lines.size() + 1, tokenizer.nextToken());
line.setOnClickListener(lineHighlighter);
lines.add(line);
}
return lines;
}
private String getHighlightedSourceCode() {
if (highlightedSourceCode == null) {
highlightedSourceCode = highlightSourceCode();
}
return highlightedSourceCode;
}
private String highlightSourceCode() {
String code = replaceTabs();
if (ApplicationSettings.highlightSources()) {
return SYNTAX_HIGHLIGHTER.highlight(code, language);
} else {
return code;
}
}
private String replaceTabs() {
StringBuilder tabReplacement = new StringBuilder();
for (int i = 0; i < ApplicationSettings.getTabSize(); i++) {
tabReplacement.append(" ");
}
return sourceCode.replace("\t", tabReplacement.toString());
}
public String getIdentifier() {
return identifier;
}
public void addTextComment(String comment) throws CommentNotAddedException {
if (selectedLine == null) {
throw new NoSelectedLineException();
}
getSelectedLine().addTextComment(comment);
}
public void addVoiceComment(File recordedFile) throws CommentNotAddedException {
if (selectedLine == null) {
throw new NoSelectedLineException();
}
getSelectedLine().createVoiceComment(recordedFile);
}
public Line getSelectedLine() {
return selectedLine;
}
public void markConversation(Line line) {
// Nothing to do, it's perfect.
}
public static SourceFile createFromString(String sourceCode, String language) {
return new SourceFile(sourceCode, language);
}
/**
* Creates source file based on a file reference.
*/
public static SourceFile createFromFile(File sourceFile) throws IOException {
String sourceCode = FileUtils.read(sourceFile);
return createFromString(sourceCode, FileUtils.getExtension(sourceFile.getName()));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment