// Copyright 2004-2014 Jim Voris // // 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 com.qumasoft.qvcslib; import java.io.IOException; import java.io.RandomAccessFile; import java.io.Serializable; import java.text.DecimalFormat; import java.util.logging.Level; import java.util.logging.Logger; /** * Label info. * @author Jim Voris */ public class LabelInfo implements Serializable { private static final long serialVersionUID = 3108618625061087277L; // Create our logger object private static final Logger LOGGER = Logger.getLogger("com.qumasoft.qvcslib"); private CommonShort revisionCount = new CommonShort(); private CommonShort creatorIndex = new CommonShort(); private CommonShort labelStringSize = new CommonShort(); private LabelRevisionInfo labelRevInfo; private String labelString; /** * Default constructor. */ public LabelInfo() { } /** * Build label info using label string, revision string, etc. * @param label the label string. * @param revisionString the revision string. * @param floatingFlag floating label flag; true if this is to be a floating label; false otherwise. * @param creatorIdx the creator index. */ public LabelInfo(String label, String revisionString, boolean floatingFlag, int creatorIdx) { // We write it as a null terminated string labelStringSize.setValue(label.getBytes().length + 1); labelString = label; creatorIndex.setValue(creatorIdx); // Create the revision major.minor pair array based on the string we // got. labelRevInfo = new LabelInfo.LabelRevisionInfo(revisionString, floatingFlag); revisionCount.setValue(labelRevInfo.getRevisionCount()); } /** * Read from a file. Assume the stream is correctly positioned. * @param inStream the stream to read from. * @throws IOException for read problems. */ public void read(RandomAccessFile inStream) throws IOException { revisionCount.read(inStream); creatorIndex.read(inStream); labelStringSize.read(inStream); // Make and read as many major/minor objects as we need. labelRevInfo = new LabelRevisionInfo(revisionCount.getValue()); labelRevInfo.read(inStream); // Read the label string. if (labelStringSize.getValue() > 0) { byte[] lblString = new byte[labelStringSize.getValue()]; int bytesRead = inStream.read(lblString); labelString = new String(lblString, 0, labelStringSize.getValue() - 1); LOGGER.log(Level.FINEST, "Read label: " + labelString); } } /** * Write to a file. * @param outStream the stream to write to. * @throws IOException for write problems. */ public void write(RandomAccessFile outStream) throws IOException { if (revisionCount.getValue() > 0) { revisionCount.write(outStream); creatorIndex.write(outStream); labelStringSize.write(outStream); // Write as many major/minor objects as we need. labelRevInfo.write(outStream); // Write the label string. if (labelStringSize.getValue() > 0) { outStream.write(labelString.getBytes()); outStream.writeByte(0); } } } /** * Is this a floating label. * @return true if this describes a floating label; false otherwise. */ public boolean isFloatingLabel() { return labelRevInfo.isFloatingLabel(); } /** * Get the depth of the revision that this label is associated with. * @return the depth of the revision that this label is associated with. */ public int getDepth() { return revisionCount.getValue() - 1; } /** * Is this the special 'obsolete' label. * @return true if this is the 'obsolete' label. */ public boolean getIsObsolete() { return labelRevInfo.isObsolete(); } /** * Set the obsolete-ness of this label info. * @param flag true to indicate this is an obsolete label; false to clear the obsolete-ness of this label. * @param creatorIdx the creator index of the user performing this operation. * @deprecated we don't use this anymore -- at least we shouldn't be using it any more. */ public void setIsObsolete(boolean flag, int creatorIdx) { if (flag) { revisionCount.setValue(1); creatorIndex.setValue(creatorIdx); labelStringSize.setValue(1); labelString = ""; labelRevInfo = new LabelRevisionInfo(1); labelRevInfo.majorMinorArray[0] = new LabelMajorMinor(); labelRevInfo.majorMinorArray[0].majorNumber.setValue(-1); labelRevInfo.majorMinorArray[0].minorNumber.setValue(-1); } else { // The caller should throw instance away. revisionCount.setValue(0); } } /** * Get the revision string for the revision associated with this label. * @return the revision string for the revision associated with this label. */ public String getLabelRevisionString() { return labelRevInfo.toString(); } /** * Get the label string. * @return the label string. */ public String getLabelString() { return labelString; } /** * Convenience toString that reports the label string. * @return convenience toString that reports the label string. */ @Override public String toString() { return labelString; } /** * Get the creator index of the user who applied this label. * @return the creator index of the user who applied this label. */ public int getCreatorIndex() { return creatorIndex.getValue(); } /** * Get a sortable revision string for the revision associated with this label. * @return a sortable revision string for the revision associated with this label. */ public String getSortableRevisionString() { return labelRevInfo.getSortableRevisionString(); } /** * Get the major/minor pairs that describe the revision associated with this label. * @return the major/minor pairs that describe the revision associated with this label. */ public LabelMajorMinor[] getMajorMinorPairs() { return labelRevInfo.getMajorMinorPairs(); } /** * Create an inner class to handle the entire label revision information. */ public class LabelRevisionInfo implements Serializable { private static final long serialVersionUID = 6948932352797324730L; private LabelMajorMinor[] majorMinorArray; /** * Create instance using revision string and floating label flag. * @param revisionString the revision string. * @param floatingLabelFlag floating label flag. */ public LabelRevisionInfo(String revisionString, boolean floatingLabelFlag) { try { byte[] revisionArray = revisionString.getBytes(); int periodCount = 0; for (int i = 0; i < revisionArray.length; i++) { if (revisionArray[i] == '.') { periodCount++; } } int majorMinorArraySize = 1 + (periodCount / 2); LOGGER.log(Level.FINE, "revision string: " + revisionString); majorMinorArray = new LabelMajorMinor[majorMinorArraySize]; int j = 0; String[] majorMinorNumberStringArray = revisionString.split("\\."); for (int i = 0; i < majorMinorArraySize; i++) { int majorNumber = Integer.parseInt(majorMinorNumberStringArray[j++]); int minorNumber = Integer.parseInt(majorMinorNumberStringArray[j++]); majorMinorArray[i] = new LabelMajorMinor(majorNumber, minorNumber); } // If this is a floating label, then the last minor number is // set to -1. if (floatingLabelFlag) { majorMinorArray[majorMinorArray.length - 1].minorNumber.setValue(-1); } } catch (NumberFormatException e) { LOGGER.log(Level.SEVERE, "failed to construct LabelRevisionInfo!! due to exception: " + e.getClass().toString() + ": " + e.getLocalizedMessage()); throw new QVCSRuntimeException(e.getLocalizedMessage()); } } /** * A useful string representation of the label revision info. * @return a useful string representation of the label revision info. */ @Override public String toString() { StringBuilder returnString = new StringBuilder(); for (int i = 0; i < majorMinorArray.length; i++) { if (i != 0) { returnString.append("."); } returnString.append(majorMinorArray[i].toString()); } return returnString.toString(); } /** * Get the major/minor pairs for this label. * @return the major/minor pairs for this label. */ public LabelMajorMinor[] getMajorMinorPairs() { return majorMinorArray; } /** * Get a sortable revision string. * @return a sortable revision string. */ public String getSortableRevisionString() { DecimalFormat formatter = new DecimalFormat("0000"); StringBuilder resultBuffer = new StringBuilder(); for (LabelMajorMinor labelMajorMinor : majorMinorArray) { int major = labelMajorMinor.majorNumber.getValue(); int minor = labelMajorMinor.minorNumber.getValue(); resultBuffer.append(formatter.format(major)); resultBuffer.append(formatter.format(minor)); } return resultBuffer.toString(); } /** * Set the size of the major/minor pair array. * @param size the size of the major/minor pair array. */ public LabelRevisionInfo(int size) { majorMinorArray = new LabelMajorMinor[size]; } /** * Read from a file. Assume the stream is positioned correctly. * @param inStream the stream to read from. * @throws IOException on a read error. */ public void read(RandomAccessFile inStream) throws IOException { for (int i = 0; i < majorMinorArray.length; i++) { majorMinorArray[i] = new LabelMajorMinor(); majorMinorArray[i].read(inStream); } } /** * Write to a file. Assume the stream is positioned correctly. * @param outStream the stream to write to. * @throws IOException on a write error. */ public void write(RandomAccessFile outStream) throws IOException { for (LabelMajorMinor labelMajorMinor : majorMinorArray) { labelMajorMinor.write(outStream); } } /** * Does this describe a floating label. * @return true if this is a floating label; false otherwise. */ public boolean isFloatingLabel() { return majorMinorArray[majorMinorArray.length - 1].minorNumber.getValue() == (short) -1; } /** * Does this describe the special 'obsolete' label. * @return true if this is the special 'obsolete' label, false otherwise. * @deprecated we don't use these kinds of labels anymore. */ public boolean isObsolete() { return (majorMinorArray[0].majorNumber.getValue() == -1) && (majorMinorArray[0].minorNumber.getValue() == -1); } /** * Get the number of major/minor pairs that are needed to describe the revision associated with this label. * TODO -- this is a lousy name for this method. * @return the number of major/minor pairs that are needed to describe the revision associated with this label. */ public int getRevisionCount() { return majorMinorArray.length; } } /** * Create an inner class to handle the reading of the major/minor pairs. */ public class LabelMajorMinor implements Serializable { private static final long serialVersionUID = 1553056809254188868L; private CommonShort majorNumber = new CommonShort(); private CommonShort minorNumber = new CommonShort(); /** * Default constructor. */ public LabelMajorMinor() { } /** * Create an instance using the given major/minor values. * @param major the major number of this revision segment. * @param minor the minor number of this revision segment. */ public LabelMajorMinor(int major, int minor) { majorNumber.setValue(major); minorNumber.setValue(minor); } /** * A useful string representation of the label's major/minor revision info. * @return a useful string representation of the label's major/minor revision info. */ @Override public String toString() { StringBuilder returnString = new StringBuilder(); returnString.append(majorNumber.getValue()); short sMinor = (short) minorNumber.getValue(); // Only report the minor number if it's != -1 if (sMinor != (short) -1) { returnString.append(".").append(Integer.toString(minorNumber.getValue())); } return returnString.toString(); } /** * Get the major number. * @return the major number. */ public int getMajorNumber() { return majorNumber.getValue(); } /** * Get the minor number. * @return the minor number. */ public int getMinorNumber() { return minorNumber.getValue(); } /** * Read from a file. Assume the stream is positioned correctly. * @param inStream the stream to read from. * @throws IOException on a read error. */ public void read(RandomAccessFile inStream) throws IOException { majorNumber.read(inStream); minorNumber.read(inStream); } /** * Write to a file. Assume the stream is positioned correctly. * @param outStream the stream to write to. * @throws IOException on a write error. */ public void write(RandomAccessFile outStream) throws IOException { majorNumber.write(outStream); minorNumber.write(outStream); } } }