/* Copyright (c) 2011 Danish Maritime Authority.
*
* Licensed 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 dk.dma.ais.sentence;
import net.jcip.annotations.NotThreadSafe;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Class to hold a comment block
*/
@NotThreadSafe
public class CommentBlock {
private Map<String, String> parameterMap = new HashMap<>();
private int totalLines = -1;
private int lastLine = -1;
private String lastGroupId;
/**
* Add line containing comment block
*
* @param line
*/
public void addLine(String line) throws CommentBlockException {
// Make line object and parse
CommentBlockLine cbLine = new CommentBlockLine();
cbLine.parse(line);
if (cbLine.getGroupId() != null) {
// Line part of group
if (lastGroupId == null) {
// New group
lastGroupId = cbLine.getGroupId();
totalLines = cbLine.getTotalLines();
lastLine = cbLine.getLineNumber();
} else {
// Part of existing group
if (cbLine.getTotalLines() != totalLines || cbLine.getLineNumber() != lastLine + 1
|| !cbLine.getGroupId().equals(lastGroupId)) {
throw new CommentBlockException("Invalid comment block grouping");
}
lastLine = cbLine.getLineNumber();
}
// Maybe finish group
if (totalLines == lastLine) {
totalLines = -1;
lastLine = -1;
lastGroupId = null;
}
} else {
// Single line
if (lastGroupId != null) {
throw new CommentBlockException("Single line comment block in unfinished group");
}
}
// Merge values
parameterMap.putAll(cbLine.getParameterMap());
}
/**
* Get number of entries in comment block
* @return
*/
public int getSize() {
return parameterMap.size();
}
/**
* Get string value for parameter code
*
* @param parameter
* @return
*/
public String getString(String parameter) {
return parameterMap.get(parameter);
}
/**
* Add key value pair with string value
*
* @param parameter
* @param value
*/
public void addString(String parameter, String value) {
parameterMap.put(parameter, value);
}
/**
* Add integer value
*
* @param parameter
* @param value
*/
public void addInt(String parameter, int value) {
parameterMap.put(parameter, Integer.toString(value));
}
/**
* Add timestamp
*
* @param timestamp
*/
public void addTimestamp(Date timestamp) {
parameterMap.put("c", Long.toString(timestamp.getTime() / 1000));
}
/**
* Get integer value of parameter code
*
* @param parameter
* @return
*/
public Integer getInt(String parameter) {
String val = parameterMap.get(parameter);
try {
return val == null ? null : Integer.parseInt(val);
} catch (NumberFormatException e) {
return null;
}
}
/**
* Get long value of parameter code
*
* @param parameter
* @return
*/
public Long getLong(String parameter) {
String val = parameterMap.get(parameter);
try {
return val == null ? null : Long.parseLong(val);
} catch (NumberFormatException e) {
return null;
}
}
/**
* Get timestamp in tag
*
* @return
*/
public Long getTimestamp() {
return getLong("c");
}
/**
* Determine if parameter exists in comment block
*
* @param parameter
* @return
*/
public boolean contains(String parameter) {
return parameterMap.containsKey(parameter);
}
/**
* Determine if a line contains comment block
*
* @param line
* @return
*/
public static boolean hasCommentBlock(String line) {
return line.length() > 0 && line.charAt(0) == '\\';
}
/**
* Determine if comment block is completed. That is no unfinished groups.
*
* @return
*/
public boolean isFinished() {
return lastGroupId == null;
}
/**
* Get id of last group
*
* @return
*/
public String getLastGroupId() {
return lastGroupId;
}
/**
* Get total number of lines in last group
*
* @return
*/
public int getTotalLines() {
return totalLines;
}
/**
* Determine if comment block contains any key value pairs
*
* @return
*/
public boolean isEmpty() {
return parameterMap.size() == 0;
}
/**
* Encode comment block in 80 character lines
*
* @return
*/
public String encode() {
return encode(80);
}
/**
* Encode comment block in a number of lines
*
* @param maxLen
* Maximum line length
* @return
*/
public String encode(int maxLen) {
// Get all pairs
List<String> pairs = new ArrayList<>();
for (Entry<String, String> pair : parameterMap.entrySet()) {
// Skip grouping tags
if (!pair.getKey().equals("g") && !pair.getKey().matches("\\d+G\\d+")) {
pairs.add(pair.getKey() + ":" + pair.getValue());
}
}
// List of lines
List<List<String>> lines = new ArrayList<>();
// Max len when accommodating for wrapping
int actualMaxLen = maxLen - 5;
List<String> currentLine = new ArrayList<>();
lines.add(currentLine);
int currentRemaining = actualMaxLen;
// Less than optimal way to separate into lines
for (String pair : pairs) {
if (pair.length() > actualMaxLen) {
throw new IllegalArgumentException("maxLen to small to accomodate pair: " + pair);
}
if (pair.length() > currentRemaining) {
currentLine = new ArrayList<>();
lines.add(currentLine);
currentLine.add(pair);
currentRemaining = actualMaxLen - pair.length();
} else {
currentLine.add(pair);
currentRemaining -= pair.length();
}
}
return wrapLines(lines);
}
private String wrapLines(List<List<String>> lines) {
List<String> strLines = new ArrayList<>();
for (List<String> line : lines) {
String lineStr = StringUtils.join(line, ",");
int checksum = 0;
for (int i = 0; i < lineStr.length(); i++) {
checksum ^= lineStr.charAt(i);
}
strLines.add("\\" + lineStr + "*" + Sentence.getStringChecksum(checksum) + "\\");
}
return StringUtils.join(strLines, "\r\n");
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[CommentBlock:");
for (String parameterCode : parameterMap.keySet()) {
buf.append(" " + parameterCode + ":" + parameterMap.get(parameterCode));
}
buf.append("]");
return buf.toString();
}
}