Java - How to measure a Matcher processing - java

Suppose that I got a brilliant idea of making a html link tag parser in order to explore the internet and i use a regex to parse and capture each occurrence of a link in a page. This code currently works fine, but I am seeking to add some members to reflect the "operation status".
public class LinkScanner {
private static final Pattern hrefPattern = Pattern.compile("<a\\b[^>]*href=\"(.*?)\".*?>(.*?)</a>");
public Collection<String> scan(String html) {
ArrayList<String> links = new ArrayList<>();
Matcher hrefMatcher = hrefPattern.matcher(html);
while (hrefMatcher.find()) {
String link = hrefMatcher.group(1);
links.add(link);
}
return links;
}
}
How I can measure this process?
For example : consider this an hypothetic measurement implementation...
public class LinkScannerWithStatus {
private int matched;
private int total;
public Collection<String> scan(String html) {
ArrayList<String> links = new ArrayList<>();
Matcher hrefMatcher = hrefPattern.matcher(html);
total = hrefMatcher.getFindCount(); // Assume getFindCount exists
while (hrefMatcher.find()) {
String link = hrefMatcher.group(1);
links.add(link);
matched++; // assume is a linear measurement mechanism
}
return links;
}
}
I don't know where to start.. I don't even know if the conjunction "Matcher processing" is grammatically valid :S

Unfortunately Matcher doesn't have a listener interface to measure progress. It would probably be prohibitively expensive to have one.
If you have the full page as String instance then you can use region to select regions of the page. You can use this to scan these regions in sequence. Then you can report to the user which part you are currently scanning. You may have to backtrack a bit to allow overlap of the regions.
You could optimize if you backtrack by using hitEnd to check if a match was ongoing. If it wasn't then you don't need to backtrack.
One problem is that URL's are not really limited in size, so you need to make a choice what size of URL's you care to support.
If you create a good regular expression then you should not really have to report back the progress, unless you are processing truly huge files. Even in that case the I/O should have more overhead than the scanning for HTML anchors.

Performance and memory issues aside, you can use a DOM parser to evaluate the HTML, that way, while you walk the DOM you can perform a given action.
Another possibility is to interpret the given HTML as XML and use SAX. This is efficient but assumes a structure that may not be there.

As requested by Victor I'll post another answer. In this case CharSequence is implemented as a wrapper around another CharSequence. As the Matcher instance requests characters the CountingCharSequence reports to a listener interface.
It's slightly dangerous to do this as CharSequence.toString() method returns a true String instance which cannot be monitored. On the other hand, it seems that the current implementation is relatively simple to implement and it does work. toString() is called, but that seems to be to populate the groups when a match has been found. Better write some unit tests around it though.
Oh, and as I have to print the "100%" mark manually there is probably a rounding error or off-by-one error. Happy debugging :P
public class RegExProgress {
// the org. LinkScanner provided by Victor
public static class LinkScanner {
private static final Pattern hrefPattern = Pattern.compile("<a\\b[^>]*href=\"(.*?)\".*?>(.*?)</a>");
public Collection<String> scan(CharSequence html) {
ArrayList<String> links = new ArrayList<>();
Matcher hrefMatcher = hrefPattern.matcher(html);
while (hrefMatcher.find()) {
String link = hrefMatcher.group(1);
links.add(link);
}
return links;
}
}
interface ProgressListener {
void listen(int characterOffset);
}
static class SyncedProgressListener implements ProgressListener {
private final int size;
private final double blockSize;
private final double percentageOfBlock;
private int block;
public SyncedProgressListener(int max, int blocks) {
this.size = max;
this.blockSize = (double) size / (double) blocks - 0.000_001d;
this.percentageOfBlock = (double) size / blockSize;
this.block = 0;
print();
}
public synchronized void listen(int characterOffset) {
if (characterOffset >= blockSize * (block + 1)) {
this.block = (int) ((double) characterOffset / blockSize);
print();
}
}
private void print() {
System.out.printf("%d%%%n", (int) (block * percentageOfBlock));
}
}
static class CountingCharSequence implements CharSequence {
private final CharSequence wrapped;
private final int start;
private final int end;
private ProgressListener progressListener;
public CountingCharSequence(CharSequence wrapped, ProgressListener progressListener) {
this.wrapped = wrapped;
this.progressListener = progressListener;
this.start = 0;
this.end = wrapped.length();
}
public CountingCharSequence(CharSequence wrapped, int start, int end, ProgressListener pl) {
this.wrapped = wrapped;
this.progressListener = pl;
this.start = start;
this.end = end;
}
#Override
public CharSequence subSequence(int start, int end) {
// this may not be needed, as charAt() has to be called eventually
System.out.printf("subSequence(%d, %d)%n", start, end);
int newStart = this.start + start;
int newEnd = this.start + end - start;
progressListener.listen(newStart);
return new CountingCharSequence(wrapped, newStart, newEnd, progressListener);
}
#Override
public int length() {
System.out.printf("length(): %d%n", end - start);
return end - start;
}
#Override
public char charAt(int index) {
//System.out.printf("charAt(%d)%n", index);
int realIndex = start + index;
progressListener.listen(realIndex);
return this.wrapped.charAt(realIndex);
}
#Override
public String toString() {
System.out.printf(" >>> toString() <<< %n", start, end);
return wrapped.toString();
}
}
public static void main(String[] args) throws Exception {
LinkScanner scanner = new LinkScanner();
String content = new String(Files.readAllBytes(Paths.get("regex - Java - How to measure a Matcher processing - Stack Overflow.htm")));
SyncedProgressListener pl = new SyncedProgressListener(content.length(), 10);
CountingCharSequence ccs = new CountingCharSequence(content, pl);
Collection<String> urls = scanner.scan(ccs);
// OK, I admit, this is because of an off-by one error
System.out.printf("100%% - %d%n", urls.size());
}
}

So, to measure your progress through a document, you want to find the total number of matches, then as you go match by match, you update the progress and add them to stored links LinkedList.
You can count the total number of matches using:
int countMatches = StringUtils.countMatches(String text, String target);
So then, just look for the String "href" or maybe the tag or some other component of a link, then you will have a hopefully accurate picture of how many links you have, then you can parse them one by one. It's not ideal because it doesn't accept regex as the target parameter.

Related

Realize for loops in Java Stream for Array List?

Question : Can i realize method private void fillingArrayList() use Java Stream API (that is in one line) . The variable i is needed to define a length of String ;
I try a for each loop but it doesn't work . I need a range for loop.
import org.apache.commons.lang3.StringUtils;
public class Tolls {
public static String digitsConcatenation(short number, long times) {
return StringUtils.repeat(Character.forDigit(number, 10), Long.valueOf(times).intValue());
}
}
public class Progression {
public Progression(Digit digit) {
this.digit = digit;
this.numbers = new ArrayList<>(Long.valueOf(digit.getTimes()).intValue());
this.fillingArrayList();
}
public Optional<Long> getProgressionSum() {
return this.numbers.stream().reduce(Long::sum);
}
public List<Long> getNumbers() {
return Collections.unmodifiableList(this.numbers);
}
private void fillingArrayList() {
for (int i = 1; i <= digit.getTimes(); i++)
this.numbers.add(Long.valueOf(Tolls.digitsConcatenation(digit.getNumber(), i)));
}
private final List<Long> numbers;
private final Digit digit;
}
My Try :
private void fillingArrayList() {
Arrays.stream(this.numbers.toArray())
.forEach(i-> this.numbers.add(
Long.valueOf(Tolls.digitsConcatenation(
digit.getNumber(), (Long) i))));
}
There are some weird things in your code, like in Progression’s constructor, writing an expression like Long.valueOf(digit.getTimes()).intValue() instead of just digit.getTimes(). The con­struc­tor of ArrayList expects an int and digit.getTimes() returns an int (or a type implicitly con­vertible to int), as demonstrated with the loop condition i <= digit.getTimes().
Likewise, the expression Long.valueOf(times).intValue() within the digitsConcatenation method, which is a cast from long to int in disguise, is only necessary because you declared the second parameter of digitsConcatenation as long despite you actually need an int and the caller’s argument is an int, so you could declare it as int in the first place.
But the entire approach of using string concatenation (incorporating a 3rd party library) and parsing it back into a number is unnecessarily complicated and inefficient. Since both conversions are implicitly using the decimal system, the operation’s result is the same as multiplying the number with ten and adding the value of the digit.
So you could just use
private void fillingArrayList() {
int n = digit.getNumber();
LongStream.iterate(n, current -> current * 10 + n)
.limit(digit.getTimes()).forEach(numbers::add);
}
without any string operation.
Even better would be to change the constructor to
public Progression(Digit digit) {
this.digit = digit;
int n = digit.getNumber();
this.numbers = LongStream.iterate(n, current -> current * 10 + n)
.limit(digit.getTimes()).boxed()
.collect(Collectors.toList());
}
letting the stream produce the List<Long> instead of constructing it manually and modify it after construction.
Following should work:
IntStream.rangeClosed(1, digit.getTimes()).forEach(i -> this.numbers.add(Long.valueOf(Tolls.digitsConcatenation(digit.getNumber(), i))););

text adventure/interactive fiction in java

I decided to create an account in order to ask a question I cant seem to figure out myself, or by some googling, hopefully I didn't just overlook it.
Essentially I am trying to make a text adventure game in Java, and am having a little trouble seeing how I should relate everything in the idea of objects. I have been successful in using XML stax and sending a file to the program, and using attributes and what not, to make it where the user can enter an integer associated with an option, and see if option requires an "item" or gives them an Item. I however did not take an OOP to this.
I want my new program to people able to take a string of user input in, instead of only an integer, and checking it against an array list if it exists. This is closer to the classic MUDs most may be familiar with.
I want to design it in a modular way, so I can slowly add on ideas, and more complexity to go along, so I don't want a "well it works so lets leave it alone" approach either.
Currently I simply want something close to this:
A Room object, which would have: an ID, Description, and interact-able
a Choice object (this one im not sure on) I thought about making an object to hold each rooms possible choices, both for exit, and for interact-ables
if so, the room object may need a Choice Object.
I've thought it over, tried some code, thought it over again, and every time, I keep ending up hard coding more than I feel I should, and making tons more variables than I feel are necessary, which makes me feel like i'm missing something crucial in my thinking.
I also want these rooms to be created through an inputted file, not generated in the code (so essentially the code is a story reader/crafter for any type, not one)
I have also been attempting this too long, and my solutions are becoming worse, but below was my most recent attempt at a rough Idea:
a GameManager class that takes the userInput and checks it some, before passing it along. I havent passed any data because im not sure of the approach. also im not used to regex, so some of that may also be wrong, if it is, maybe point it out, but that is not my focus
import java.util.Scanner;
public class GameManager {
private static final String EXIT_PHRASE = "exit";
public static void main(String[] args) {
Scanner userInput = new Scanner(System.in);
String userStringVal = "";
while(!userStringVal.equals(EXIT_PHRASE)){
userStringVal= userInput.nextLine();
if(checkKeywords(userStringVal)){
System.out.println("matches keyword");
}
else System.out.println("didnt match a keyword");
}
userInput.close();
}
public static boolean checkKeywords(String string){
boolean isKeyword = false;
string.toLowerCase();
if(string.matches("travel.*") || string.matches("search.*")){
System.out.println("passed first check");
String substring = string.substring(6);
if(matchDirection(substring)){
isKeyword = true;
}
}
return isKeyword;
}
public static boolean matchDirection(String string){
boolean hasDirection = false;
if(string.matches(".*\\bnorth|south|east|west|northeast|northwest|southeast| southwest|up|down")){
hasDirection = true;
}
return hasDirection;
}
}
The Room object I thought about as such:
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
public class Room {
private String roomDescription = "";
private int roomID=0;
private int northExit=0;
private int southExit=0;
private int eastExit=0;
private int westExit=0;
private int northeastExit=0;
private int northwestExit=0;
private int southeastExit=0;
private int southwestExit=0;
private int upExit=0;
private int downExit=0;
private String[] interactables = new String[10];
private Options options = new Options();
public Room(XMLStreamReader reader) throws XMLStreamException{
setAttValues(reader);
setRoomDescription(reader);
setUpOptions();
}
public void setinteractables(XMLStreamReader reader){
int count = reader.getAttributeCount();
for(int i = 0; i < count; i++){
interactables[i] = reader.getAttributeValue(i);
}
}
public void setAttValues(XMLStreamReader reader){
int count = reader.getAttributeCount();
for(int i = 0; i < count; i++){
String att = reader.getAttributeLocalName(i);
if(att !=""){
switch(att){
case "North": northExit=Integer.parseInt(att);
case "South": southExit=Integer.parseInt(att);
case "East": eastExit=Integer.parseInt(att);
case "West": westExit=Integer.parseInt(att);
case "NorthEast": northeastExit=Integer.parseInt(att);
case "NorthWest": northwestExit=Integer.parseInt(att);
case "SouthEast": southeastExit=Integer.parseInt(att);
case "SouthWest": southwestExit=Integer.parseInt(att);
case "Up": upExit=Integer.parseInt(att);
case "Down": downExit=Integer.parseInt(att);
case "ID": roomID=Integer.parseInt(att);
}
}
}
}
public void setRoomDescription(XMLStreamReader reader) throws XMLStreamException{
roomDescription = reader.getElementText();
}
public void setUpOptions(){
options.setCardinalPointers(northExit, southExit, eastExit, westExit);
options.setIntercardinalPointers(northeastExit, northwestExit, southeastExit, southwestExit);
options.setElevationPointers(upExit, downExit);
}
}
what can I do to make sure I dont have to state so many directions with so many variables?
here is a quick and rough idea of an Option class that I thought about, but i didn't finish deciding I am already too far in the wrong direction
public class Options {
private int northPointer = 0;
private int southPointer= 0;
private int eastPointer = 0;
private int westPointer = 0;
private int northeastPointer= 0;
private int northwestPointer = 0;
private int southeastPointer = 0;
private int southwestPointer = 0;
private int upPointer = 0;
private int downPointer = 0;
private String northInteractable = "";
private String southInteractable = "";
private String eastInteractable = "";
private String westInteractable = "";
private String northeastInteractable ="";
private String northwestInteractable = "";
private String southeastInteractable = "";
private String southwestInteractable = "";
private String upInteractable = "";
private String downInteractable = "";
public Options(){
}
public void setCardinalPointers(int north, int south, int east, int west){
northPointer = north;
southPointer = south;
eastPointer = east;
westPointer = west;
}
public void setIntercardinalPointers(int northeast, int northwest, int southeast, int southwest){
northeastPointer = northeast;
northwestPointer=northwest;
southeastPointer=southeast;
southwestPointer=southwest;
}
public void setElevationPointers(int up, int down){
upPointer = up;
downPointer = down;
}
public String whatToReturn(String string){
String importantPart = "";
if(string.matches("travel.*")){
String substring = string.substring(6);
}
else {
importantPart = "Interactable";
String substring = string.substring(6);
if (substring.matches("\\bnorth\\b")) {
if(northInteractable!=0){
}
}
else if (substring.matches("\\bsouth\\b"))
else if (substring.matches("\\beast\\b"))
else if (substring.matches("\\bwest\\b"))
else if (substring.contains("northeast"))
else if (substring.contains("northwest"))
else if (substring.contains("southeast"))
else if (substring.contains("southwest"))
else if (substring.contains("up"))
else if (substring.contains("down"))
}
return importantPart;
}
}
I did not see the adventure tag until after I typed this, so I will start perusing through there, but will still post this, so my apologies if there is a good answer to this and I have yet to find it.
as a recap: what would be a good way to relate a few objects to create a room object (that gets its information from a file (XML being what im used to)) having exits, descriptions, and interactions. and the user interacting with these based off keywords that can be inputted freely, and not restricted to say, index values of array's holding keywords.
Im thinking when the user types something like "travel north" to first check if they typed a keyword, in this case being travel, then a direction. Then somewhree else checking if it states travel, check north with a possible northExit a room may or may not have. Then if its another keyword, say like check, to make it easy also have the exact same directions, but check for a different string.
Then if room "northExit" exists, get an option somehow, with a pointer to another roomID. though This thought process causes me issues when thinking about future possibility of requiring items for getting to the next room. Also where to store/acquire these options is causing some difficulties.
There are two things I would like to introduce to you. The first, in the enum. You can think of this as a special kind of class where all the possible options are enumerated in the class definition. This is perfect for things like, in your case, directions. Enums can be simple, where you just list all of the possible options for use in other classes:
public enum Direction {
NORTH, NORTH_EAST, EAST, SOUTH_EAST, SOUTH, SOUTH_WEST, WEST, NOTH_WEST;
}
They can be a bit more complex, if you want them to have methods and attributes of their own:
public enum Direction {
NORTH(true), NORTH_EAST(false), EAST(true), SOUTH_EAST(false), SOUTH(true), SOUTH_WEST(false), WEST(true), NOTH_WEST(false);
private final boolean isCardinal;
private Direction(boolean isCardinal){
this.isCardinal = isCardinal;
}
public boolean isCardinal(){
return isCardinal;
}
public static Collection<Direction> getCardinalDirections(){
return Arrays.asList(Direction.values()).stream().filter(Direction::isCardinal).collect(Collectors.toList());
}
public static Collection<Direction> getIncardinalDirections(){
return Arrays.asList(Direction.values()).stream().filter(x -> !x.isCardinal()).collect(Collectors.toList());
}
}
Please read more about Java enum types here.
The second thing I would like to introduce to you is the data structure known as the Map. Maps are also known as Dictionaries, and that can often help understanding how they work. A Map will take one object and map it to another object, like how a Dictionary maps a word to its definition, or a phonebook maps a person's name to their phone number. We can simplify your Room class a ton by using a Map. I am not going to reproduce all of your code, since I'm focusing on your Room exists right now:
public class Room {
private Map<Direction, Room> exits;
public Room(){
this.exits = new HashMap<>();
}
public void setExit(Direction direction, Room room){
this.exits.put(direction, room);
}
public Room getExit(Direction direction){
return this.exits.get(direction);
}
}
Please read more about the Java Map interface here.
You will, of course, need to adapt your methods which are reading from XML, etc. But, now, your Room class should be greatly simplified.
I hope this points you in a helpful direction.

ideas for optimizing class in Java

This is a class I've written and it feels 'clunky', like there should be a better way to set this up without needing the extra method setList() to instantiate the array. I'm trying to only leave in the parts relevant to my question, as well as an example of what I did the first time that threw a runtime (not compiletime) error. I'm still mostly used to interpreted languages, so the stricter rules of Java are taking some getting used to.
public class Numbersplode
{
// fields
private int before;
private int goal;
private int[] processed;
// constructors
Numbersplode(int begin, int finish)
{
this.before = begin;
this.goal = finish;
this.processed = this.setList(begin);
this.transList();
}
// mutators
private int[] setList(int begin)
{
int[] result = new int[begin];
return result;
}
public void transList()
{
// transforms the list
int count;
double temp;
for (count = 0; count < this.before; count++)
{
temp = (double)count/(double)this.before * (double)this.goal;
this.processed[count] = (int)temp;
}
}
}
It seems like I should be able to avoid having the setList() method, but when I tried this (everything else the same):
public class Numbersplode
{
// fields
private int before;
private int goal;
private int[] processed = new int[before];
// constructors
Numbersplode(int begin, int finish)
{
this.before = begin;
this.goal = finish;
this.transList();
}
[..............]
I receive java.lang.ArrayIndexOutOfBoundsException: 0 since processed[] apparently can't be defined that way.
That extra class seems to solve the problem, but it seems to me that the Constructor should define those variables all at the same time of object creation, thus allowing for the array processed to be defined at the same time in that way.
So is there a more elegant solution I'm missing? If I find one before this is solved I'll post it up here.
EDIT
Just to be clear, if I compile the class (or even a program that creates an object from that class) I don't have any problems until I actually run the program (thus the runtime problem vs compiletime, but wanted to be clear)
And why even have a setList() method -- a private(?) mutator. Why not simply set processed = new int[before] in your constructor?
Numbersplode(int before, int goal) {
this.before = before;
this.goal = goal;
processed = new int[before];
transList();
}

How to get the positions of all matches in a String?

I have a text document and a query (the query could be more than one word). I want to find the position of all occurrences of the query in the document.
I thought of the documentText.indexOf(query) or using regular expression but I could not make it work.
I end up with the following method:
First, I have create a dataType called QueryOccurrence
public class QueryOccurrence implements Serializable{
public QueryOccurrence(){}
private int start;
private int end;
public QueryOccurrence(int nameStart,int nameEnd,String nameText){
start=nameStart;
end=nameEnd;
}
public int getStart(){
return start;
}
public int getEnd(){
return end;
}
public void SetStart(int i){
start=i;
}
public void SetEnd(int i){
end=i;
}
}
Then, I have used this datatype in the following method:
public static List<QueryOccurrence>FindQueryPositions(String documentText, String query){
// Normalize do the following: lower case, trim, and remove punctuation
String normalizedQuery = Normalize.Normalize(query);
String normalizedDocument = Normalize.Normalize(documentText);
String[] documentWords = normalizedDocument.split(" ");;
String[] queryArray = normalizedQuery.split(" ");
List<QueryOccurrence> foundQueries = new ArrayList();
QueryOccurrence foundQuery = new QueryOccurrence();
int index = 0;
for (String word : documentWords) {
if (word.equals(queryArray[0])){
foundQuery.SetStart(index);
}
if (word.equals(queryArray[queryArray.length-1])){
foundQuery.SetEnd(index);
if((foundQuery.End()-foundQuery.Start())+1==queryArray.length){
//add the found query to the list
foundQueries.add(foundQuery);
//flush the foundQuery variable to use it again
foundQuery= new QueryOccurrence();
}
}
index++;
}
return foundQueries;
}
This method return a list of all occurrence of the query in the document each one with its position.
Could you suggest any easer and faster way to accomplish this task.
Thanks
Your first approach was a good idea, but String.indexOf does not support regular expressions.
Another easier way which uses a similar approach, but in a two step method, is as follows:
List<Integer> positions = new ArrayList();
Pattern p = Pattern.compile(queryPattern); // insert your pattern here
Matcher m = p.matcher(documentText);
while (m.find()) {
positions.add(m.start());
}
Where positions will hold all the start positions of the matches.

Java enum alternative to how I did this?

Teaching myself Java by coding a MIDI handling program. One thing the program needs to be able to do is convert back and forth between MIDI note numbers and their corresponding compact string representations. I looked at using an enum setup, but due to naming constraints you can't do something like
c-1, c#-1, ... g9;
because of the sharps and negatives (yes, I'm following the convention that makes you end up with a negative octave :P).
It seemed clunky to have to make a conversion between what's allowed and what I want.
CNEG1("c-1"),
CSNEG1("c#-1"),
DNEG1("d-1"),
...
G9("g9");
So I came up with the static imports scheme below, and it works fine. However, I want to learn more about how to use enums, and I have a hunch that they might actually be somehow better suited to the task - if only I understood the ins and outs better. So that's my question: can anyone come up with an elegant way to provide the same functionality using an enum scheme? Moreover, would there be a strong argument for doing so?
public abstract class MethodsAndConstants {
public static final String TONICS[] = {"c","c#","d","d#","e","f","f#","g","g#","a","a#","b"};
static final NoteMap notemap = new NoteMap();
static class NoteMap{
static String map[] = new String[128];
NoteMap() {
for (int i = 0; i < 128; i++){
int octave = i/12 - 1;
String tonic = MethodsAndConstants.TONICS[i%12];
map[i] = tonic + octave;
}
}
}
public static int convert_midi_note(String name){
return indexOf(NoteMap.map, name);
}
public static String convert_midi_note(int note_num){
return NoteMap.map[note_num];
}
public static int indexOf(String[] a, String item){
return java.util.Arrays.asList(a).indexOf(item);
}
}
EDIT ------------------------------------------
After heavy consideration I think in this particular situation enums might be overkill after all. I might end up just using this code down here, same sort of static import approach but no longer even requiring anything like the NoteMap business up above.
note_num -> name conversions are really straightforward, and the name -> note_num stuff is just good ol' string-parsing fun.
public abstract class MethodsAndConstants {
public static final String[] TONICS = {"c","c#","d","d#","e","f","f#","g","g#","a","a#","b"};
static String convert(int i) {
String tonic = MethodsAndConstants.TONICS[i%12];
int octave = (i / 12) - 1;
return tonic + octave;
}
static int convert(String s) {
int tonic = java.util.Arrays.asList(MethodsAndConstants.TONICS).indexOf(s.substring(0,1));
if (s.contains("#")) tonic += 1;
int octave = Integer.parseInt(s.substring(s.length()-1));
if (s.contains("-")) octave -= 2; // case octave = -1
int note_num = ((octave + 1) * 12) + tonic;
return note_num;
}
}
You could use an enum to represent the pitch, but I might try encapsulating a Pitch in a class
public class Pitch {
private final int octave;
private final Note note;
public enum Note {
C("C",4), CSHARP("C#/Db",5), DFLAT("C#/Db",5), //and so on
private final String thePitch;
private final int midiAdjust;
private Note(final String thePitch, final int midiAdjust) {
this.thePitch = thePitch;
this.midiAdjust = midiAdjust;
}
String getThePitch() {
return thePitch;
}
int getMidiAdjust() {
return midiAdjust;
}
}
public Pitch(Note note, int octave) {
this.note = note;
this.octave = octave;
}
public int getMidiNumber(){
return 12*octave + note.getMidiAdjust();
}
}
This would account for the fact that the note (C, C#, D, D#, E...) is going to be one of a repeating set, but you could have all kinds of octaves, in this case handled by an int. It would greatly reduce the size of your enum.
EDIT: I added a few lines in here as an idea. You could pass a second parameter into the constructor of the enum to allow you to return a MIDI number representing the pitch. In this one I assumed that the lowest number represented by MIDI is an A, but I may be wrong on that. Also the 12*octave is intended to add a whole octave of pitches for each increment. You will probably have to adjust this slightly, as I see you are using that one weird notation.
Something like that:
public enum Note {
CNEG1("c-1"), CSNEG1("c#-1"), DNEG1("d-1");
private final String tonicOctave;
private Note(final String tonicOctave) {
this.tonicOctave = tonicOctave;
}
public String getTonicOctave() {
return this.tonicOctave;
}
public static Note fromTonicOctave(final String val) {
for (final Note note: Note.values()) {
if (note.getTonicOctave().equals(val)) {
return note;
}
}
return null;
}
}
Note, you can have as many parameters as you need in your enum, so if you need to separate tonic and octave, you can.

Categories

Resources