SectionContent.java
/*
** Module : SectionContent.java
** Abstract : Progress 4GL INI file content class representing one section of data
**
** Copyright (c) 2013-2017 Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------Description----------------------------------
** 001 EVL 20130701 Created initial version.
** 004 MAG 20140908 Make key names case insensitive.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.util;
import java.io.*;
import java.util.*;
/**
* A class implementing the INI text file key=value section lines. Can contain several lines with
* key values and also comment lines end empty lines in any place of the section.
*
* @author EVL
*/
public class SectionContent
extends Content
{
/** Map to store key=value pars. */
private Map<String, String> keyMap =
new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
/** The section name associated with the content. */
private String name = null;
/** List to control section line ordering. */
private List<Renderable> ordering = new LinkedList<Renderable>();
/** The position of the last key=value pair in the ordering list. */
private int nextKeyNumber = 0;
/**
* Constructs an instance of the data section in INI file.
*
* @param name
* The name of the section corresponding to the key=value pairs of the INI file.
*/
public SectionContent(String name)
{
super();
this.name = name;
}
/**
* Getting the name of this section.
*
* @return The String with the section name.
*/
public String getName()
{
return name;
}
/**
* Writes the content of the data section to the file via Writer object provided.
*
* @param pw
* The writer will be used to save content to the file.
*/
public void write(PrintWriter pw)
{
if (ordering.size() > 0)
{
// Write the section name first
pw.println("[" + name + "]");
// Key names iterator
Iterator<Renderable> iterLines = ordering.iterator();
while (iterLines.hasNext())
{
// Get next available Renderable object
Renderable lineToRender = (Renderable)iterLines.next();
// And write them to the file
String nextLine = lineToRender.render();
// It is possible to have null pointer here if the key was previously removed
if (nextLine != null)
{
pw.println(nextLine);
}
}
}
}
/**
* Checks if the content of the section is empty or not.
*
* @return <code>true</code> in case of not emptry, <code>false</code> otherwise.
*/
public boolean notEmpty()
{
// Not empty means we have some elements inside allocated map
return ordering != null && ordering.size() > 0;
}
/**
* Stores the key=value pair into the section internal map.
*
* @param key
* The String key name to store.
* @param value
* The String value to be associated with the given key name.
*/
public void store(String key, String value)
{
// Find out if the key=value line already in the section
boolean alreadyInMap = alreadyInSection(key);
// Put key=value pair into internal map
keyMap.put(key, value);
// And refresh ordering list if the Renderer is still not there
if (!alreadyInMap)
{
// We have to insert new key after last existed key, not at the end of the list
// And increase then the next number
ordering.add(nextKeyNumber++, new KeyRenderer(keyMap, key));
}
}
/**
* Removes the key=value pair from the section internal map. After this method the caller
* must reload the INI file to sync up the internal maps.
*
* @param key
* The String key name to remove.
*/
public void remove(String key)
{
// Find out if the key=value line already in the section and remove them from map only
if (alreadyInSection(key))
{
keyMap.remove(key);
}
}
/**
* Adds the key=value pair to the section internals assuming this is the new key.
*
* @param key
* The String key name to store.
* @param value
* The String value to be associated with the given key name.
*/
public void add(String key, String value)
{
// Put key=value pair into internal map
keyMap.put(key, value);
// And refresh ordering list if the Renderer is still not there
ordering.add(new KeyRenderer(keyMap, key));
// Remember the position of the next key to add in store() method
nextKeyNumber = ordering.size();
}
/**
* Stores the empty line or comment iside the section content.
*
* @param textLine
* The ignored text line to store.
*/
public void store(String textLine)
{
// And refresh ordering list
ordering.add(new TextRenderer(textLine));
}
/**
* Stores the line iside the section content as pseudo key. The all next key-value pair will
* be stored after this line. Simulation of 4GL CHUI crazy apprach.
*
* @param textLine
* The ignored text line to store.
*/
public void storeAsPseudoKey(String textLine)
{
// And refresh ordering list
ordering.add(nextKeyNumber++, new TextRenderer(textLine));
}
/**
* Gets the value associated with the given key inside the section.
*
* @param key
* The String key name of the value to be retrieved.
*
* @return The String value associate with the given key or <code>null</code> if there is no
* key=value pair inside the section.
*/
public String getValueForKey(String key)
{
if (keyMap != null)
{
return keyMap.get(key);
}
return null;
}
/**
* Checks if the given key already added to the section internals..
*
* @param key
* The String key name to check.
*
* @return <code>true</code> if the key was already added to internal map, <code>false</code>
* otherwise.
*/
public boolean alreadyInSection(String key)
{
if (keyMap != null)
{
return keyMap.containsKey(key);
}
return false;
}
/**
* Getting the comma-separated list of the keys for this section.
*
* @return The String with comma-separated key names.
*/
public String getKeys()
{
StringBuilder sb = new StringBuilder();
Iterator<Renderable> iterLines = ordering.iterator();
int count = 0;
while (iterLines.hasNext())
{
// Get next available Renderable object
Renderable lineToCheck = (Renderable)iterLines.next();
// Get the string internal of the renderable, key or text members
String keyToCheck = lineToCheck.toString();
// And check if it is a key from key-value map
if (alreadyInSection(keyToCheck))
{
// This is the comma-separator if there is more than 1 name
if (count > 0)
{
sb.append(EnvironmentOps.PROPATH_SEPARATOR);
}
sb.append(keyToCheck);
count++;
}
}
return sb.toString();
}
/**
* The interface to present single line and key-value pair in one ordered list.
*/
public interface Renderable
{
/**
* Gets the string representation of the instance.
*
* @return The String value associated with the given key=value line of the file or line
* to be ignored.
*/
public String render();
/**
* Gets the string representation of the instance internal string part.
*
* @return The String value associated with the given key or whole line.
*/
public String toString();
}
/**
* The class to present single line in one ordered list.
*/
private static class TextRenderer
implements Renderable
{
/** The content of the whole line. */
private String text = null;
/**
* Constructs an instance of the single line in INI file.
*
* @param text
* The text corresponding to the ignored line of the INI file.
*/
private TextRenderer(String text)
{
this.text = text;
}
/**
* Gets the string representation of the ignored line.
*
* @return The String value associated with the given to be ignored.
*/
public String render()
{
return text;
}
/**
* Gets the string representation of the instance internal string part.
*
* @return The String value associated with the given key or whole line.
*/
public String toString()
{
return render();
}
}
/**
* The class to present key-value pair in one ordered list.
*/
private static class KeyRenderer
implements Renderable
{
/** The key name.*/
private String key = null;
/** The Map containing key=value pairs.*/
private Map<String, String> lookup = null;
/**
* Constructs an instance of the key entry line in INI file.
*
* @param lookup
* The Map containing the key-value pairs to look for.
* @param key
* The key part string of the given key=value pair.
*/
private KeyRenderer(Map<String, String> lookup, String key)
{
this.lookup = lookup;
this.key = key;
}
/**
* Gets the string representation of the key=value pair.
*
* @return The String value associated with the given key=value line of the file.
*/
public String render()
{
// Get the value for the known key
String value = lookup.get(key);
if (value != null)
return key + "=" + value;
else
return null;
}
/**
* Gets the string representation of the instance internal string part.
*
* @return The String value associated with the given key or whole line.
*/
public String toString()
{
return key;
}
}
}