All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.ripe.db.whois.common.rpsl.RpslAttribute Maven / Gradle / Ivy

package net.ripe.db.whois.common.rpsl;

import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;

import net.ripe.db.whois.common.domain.CIString;
import net.ripe.db.whois.common.rpsl.attrs.MntRoutes;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang3.tuple.Pair;

import javax.annotation.CheckForNull;
import javax.annotation.concurrent.Immutable;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import static net.ripe.db.whois.common.domain.CIString.ciImmutableSet;
import static net.ripe.db.whois.common.domain.CIString.ciString;

@Immutable
public final class RpslAttribute {
    private static final int LEADING_CHARS = 16;
    private static final int LEADING_CHARS_SHORTHAND = 5;

    private final AttributeType type;
    private final String key;
    private final String value;     // non-clean, contains EOL comments too
    private String cleanComment;

    private int hash;
    private Set cleanValues;

    public RpslAttribute(final AttributeType attributeType, final CIString value) {
        this(attributeType, value.toString());
    }

    public RpslAttribute(final AttributeType attributeType, final String value) {
        Validate.notNull(attributeType);
        Validate.notNull(value);
        this.key = attributeType.getName();
        this.value = value;
        this.type = attributeType;
    }

    public RpslAttribute(final String key, final CIString value) {
        this(key, value.toString());
    }

    public RpslAttribute(final String key, final String value) {
        Validate.notNull(key);
        Validate.notNull(value);
        this.key = key.toLowerCase();
        this.value = value;
        this.type = AttributeType.getByNameOrNull(this.key);
    }

    public String getKey() {
        return key;
    }

    public String getValue() {
        return value;
    }

    public String getCleanComment() {
        if (cleanValues == null) {
            extractCleanValueAndComment(value);
        }
        return cleanComment;
    }
    
    /**
     * Return the list of tokens captured by {@link AttributeLexerWrapper} in the attribute instance
     * @see AttributeLexerWrapper#parse(java.io.Reader)
     */
    public List>> getTokenList() {
    	try {
			return AttributeLexerWrapper.parse(this);
		} catch (ClassNotFoundException e) {
			return new LinkedList>>();
		}
    }

    public CIString getCleanValue() {
        final Set values = getCleanValues();
        switch (values.size()) {
            case 0:
                throw new IllegalStateException("No " + type + ": value found");
            case 1:
                return values.iterator().next();
            default:
                throw new IllegalStateException("Multiple " + type + ": values found");
        }
    }

    // TODO: [AH] should NOT return empty values; however, that behavior breaks validateSyntax() as it also relies on this method, and can't validate list structure if empty values are silently omitted
    public Set getCleanValues() {
        if (cleanValues == null) {
            extractCleanValueAndComment(value);
        }

        return cleanValues;
    }

    /**
     * Get all reference values.
     * 

* A reference value is the cleaned value. If part of a clean value references another object, * the rest of that value is discarded and only the reference is retained. */ public Set getReferenceValues() { if (AttributeType.MNT_ROUTES.equals(type)) { final Set values = getCleanValues(); final Set result = Sets.newLinkedHashSetWithExpectedSize(values.size()); for (final CIString value : values) { result.add(MntRoutes.parse(value).getMaintainer()); } return result; } else { return getCleanValues(); } } /** * Get the reference value. *

* Reference values are explaned in the {@link #getReferenceValues() getReferenceValues} method. * * @throws IllegalStateException If there is not exactly one reference value. */ public CIString getReferenceValue() { final Set values = getReferenceValues(); switch (values.size()) { case 0: throw new IllegalStateException("No value found"); case 1: return values.iterator().next(); default: throw new IllegalStateException("Multiple reference values found: " + values); } } private void extractCleanValueAndComment(final String value) { final StringBuilder cleanedValue = new StringBuilder(value.length()); final StringBuilder commentValue = new StringBuilder(value.length()); boolean comment = false; boolean space = false; boolean newline = false; boolean valueWritten = false; boolean commentWritten = false; for (final char c : value.toCharArray()) { if (c == '\n') { newline = true; space = true; comment = false; continue; } if (newline) { newline = false; if (c == '+') { continue; } } if (c == '#') { comment = true; continue; } if (c == ' ' || c == '\t' || c == '\r') { space = true; continue; } if (comment) { if (commentWritten) { if (space) { commentValue.append(' '); space = false; } } else { commentWritten = true; space = false; } commentValue.append(c); continue; } if (valueWritten) { if (space) { cleanedValue.append(' '); space = false; } } else { valueWritten = true; space = false; } cleanedValue.append(c); } this.cleanComment = commentWritten ? commentValue.toString() : null; if (type == null) { cleanValues = Collections.singleton(ciString(cleanedValue.toString())); } else { cleanValues = ciImmutableSet(type.splitValue(cleanedValue.toString())); } } public void validateSyntax(final ObjectType objectType, final ObjectMessages objectMessages) { for (final CIString cleanValue : getCleanValues()) { if (!type.getSyntax().matches(objectType, cleanValue.toString())) { objectMessages.addMessage(this, ValidationMessages.syntaxError(cleanValue.toString())); } } } public void writeTo(final Writer writer) throws IOException { writer.write(key); writer.write(':'); writeAttributeValueTo(writer); writer.write('\n'); } /** value as it is written to mysql/port43 client */ public String getFormattedValue() { try { final StringWriter writer = new StringWriter(); writeAttributeValueTo(writer); return writer.toString(); } catch (IOException e) { throw new IllegalStateException("Should never occur", e); } } public void writeAttributeValueTo(final Writer writer) throws IOException { final int column = key.startsWith("*") ? LEADING_CHARS_SHORTHAND : LEADING_CHARS; final char[] chars = value.toCharArray(); int leadColumn = key.length() + 1; int spaces = 0; for (final char c : chars) { if (leadColumn == 0 && spaces == 0 && c == '+') { writer.write(c); leadColumn++; } else if (c == ' ' || c == '\t' || c == '\r') { spaces++; } else if (c == '\n') { if (spaces > 0 && leadColumn == 0) { writer.write('+'); } leadColumn = 0; spaces = 0; writer.write(c); } else { if (leadColumn < column) { spaces = column - leadColumn; leadColumn = column; } while (spaces > 0) { writer.write(' '); spaces--; } writer.write(c); } } if (spaces > 0 && leadColumn == 0) { writer.write('+'); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final RpslAttribute attribute = (RpslAttribute) o; if (type == null) { if (attribute.type != null) { return false; } return key.equals(attribute.key); } else { if (type != attribute.type) { return false; } return Iterables.elementsEqual(getCleanValues(), attribute.getCleanValues()); } } @Override public int hashCode() { if (hash == 0) { hash = 31 * key.hashCode() + getCleanValues().hashCode(); } return hash; } @CheckForNull public AttributeType getType() { return type; } @Override public String toString() { try { final StringWriter writer = new StringWriter(); writeTo(writer); return writer.toString(); } catch (IOException e) { throw new IllegalStateException("Should never occur", e); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy