C# - NAudio - How to change sample rate on a float[] while reading it? - naudio

I'm coding my first audio application, and I'm struggling for hours on trying to change samplerate of a cached sound.
I'm using NAudio and I was able to change the Volume, tweaking the Read() method of my ISampleProvider.
Here is the CachedSound Class :
public class CachedSound
{
public float[] AudioData { get; private set; }
public WaveFormat WaveFormat { get; set; }
public CachedSound(string audioFileName)
{
using (var audioFileReader = new AudioFileReader(audioFileName))
{
WaveFormat = audioFileReader.WaveFormat;
var wholeFile = new List<float>((int)(audioFileReader.Length / 4));
var readBuffer = new float[audioFileReader.WaveFormat.SampleRate * audioFileReader.WaveFormat.Channels];
int samplesRead;
while ((samplesRead = audioFileReader.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
wholeFile.AddRange(readBuffer.Take(samplesRead));
}
AudioData = wholeFile.ToArray();
}
}
}
And here is the CachedSoundSampleProvider class :
using NAudio.Wave;
using System;
public delegate void PlaybackEndedHandler();
public class CachedSoundSampleProvider : ISampleProvider
{
public event PlaybackEndedHandler PlaybackEnded;
private CachedSound cachedSound;
private long _position;
public long Position {
get { return _position; }
set { _position = value; }
}
private float _volume;
public float Volume {
get { return _volume; }
set { _volume = value; }
}
private float _pitchMultiplicator;
public float PitchMultiplicator
{
get { return _pitchMultiplicator; }
set { _pitchMultiplicator = value; }
}
public WaveFormat OriginalWaveFormat { get; set; }
public WaveFormat WaveFormat {
get { return cachedSound.WaveFormat; }
}
//Constructeur
public CachedSoundSampleProvider(CachedSound _cachedSound)
{
cachedSound = _cachedSound;
OriginalWaveFormat = WaveFormat;
}
public int Read(float[] destBuffer, int offset, int numBytes)
{
long availableSamples = cachedSound.AudioData.Length - Position;
long samplesToCopy = Math.Min(availableSamples, numBytes);
//Changing original audio data samplerate
//Double speed to check if working
cachedSound.WaveFormat = new WaveFormat(cachedSound.WaveFormat.SampleRate*2, cachedSound.WaveFormat.Channels);
Array.Copy(cachedSound.AudioData, Position, destBuffer, offset, samplesToCopy);
//Changing Volume
for (int i = 0; i < destBuffer.Length; ++i)
destBuffer[i] *= (Volume > -40) ? (float)Math.Pow(10.0f, Volume * 0.05f) : 0;
Position += samplesToCopy;
if (availableSamples == 0) PlaybackEnded();
return (int)samplesToCopy;
}
}
I don't know how I can achieve this yet.
My goal is simple, I want to be able to tweak sample rate at realtime.
I read it's impossible to change it on the ISampleProvider interface.
That's why I tried to change it on the original audioData.
Thanks in advance for your help ! :)

Related

How to set both axis's scales to be always the same?

MPAndroidChart allows you to zoom in the X axis, Y axis and both. I would like to rescale the remaining axis (or both) to match the one(s) being scaled.
For that I've created a OnChartGestureListener:
public class ZoomNotDistorting implements OnChartGestureListener {
private Chart chart;
private ViewPortHandler viewPortHandler;
private float startDist = 1f;
private float scaleX, scaleY;
public ZoomNotDistorting(Chart chart) {
this.chart = chart;
this.viewPortHandler = chart.getViewPortHandler();
}
#Override
public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
int action = me.getAction() & MotionEvent.ACTION_MASK;
if(action == MotionEvent.ACTION_POINTER_DOWN && me.getPointerCount() >= 2) {
startDist = spacing(me);
}
}
#Override
public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
switch (lastPerformedGesture) {
case PINCH_ZOOM:
float scale = spacing(me) / startDist; // total scale
boolean isZoomingOut = (scale < 1);
if(isZoomingOut) {
if(scaleX < scaleY) {
viewPortHandler.zoom(scaleX, scaleX);
} else {
viewPortHandler.zoom(scaleY, scaleY);
}
} else {
if(scaleX > scaleY) {
viewPortHandler.zoom(scaleX, scaleX);
} else {
viewPortHandler.zoom(scaleY, scaleY);
}
}
break;
case X_ZOOM:
viewPortHandler.zoom(scaleX, scaleX);
break;
case Y_ZOOM:
viewPortHandler.zoom(scaleY, scaleY);
break;
}
chart.invalidate();
}
#Override
public void onChartLongPressed(MotionEvent me) {}
#Override
public void onChartDoubleTapped(MotionEvent me) {}
#Override
public void onChartSingleTapped(MotionEvent me) {}
#Override
public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) {}
#Override
public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
this.scaleX = scaleX;
this.scaleY = scaleY;
}
#Override
public void onChartTranslate(MotionEvent me, float dX, float dY) {}
/**
* returns the distance between two pointer touch points
*/
private static float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
}
That class doesn't seem to be doing anything,How to set both axis's scales to be always the same?
Also, here is my chart class:
public class MathGraph extends LineChart {
public static final int BOUNDARIES = 100;
public static final int DATA_POINTS = 200;
private static final int LINE_WIDTH = 2;
private LineData data;
public MathGraph(Context context, AttributeSet attrs) {
super(context, attrs);
super.setDescription(null);
//Misc
getLegend().setEnabled(false);
setRenderer(new LineChatRendererNoData(this, mAnimator, mViewPortHandler));
//Lines encasing the chart
getXAxis().setAxisLineWidth(LINE_WIDTH);
getAxisLeft().setAxisLineWidth(LINE_WIDTH);
getAxisRight().setEnabled(false);
//Line for (x; 0)
getAxisLeft().setDrawZeroLine(true);
getAxisLeft().setZeroLineWidth(LINE_WIDTH);
getAxisRight().setDrawZeroLine(true);
getAxisRight().setZeroLineWidth(LINE_WIDTH);
//Line for (0; y)
LimitLine limitLine = new LimitLine(0f);
limitLine.setLineColor(Color.GRAY);
limitLine.setLineWidth(LINE_WIDTH);
getXAxis().addLimitLine(limitLine);
setOnChartGestureListener(new ZoomNotDistorting(this));
}
public void setFunction(Function f) {
if(!f.checkSyntax()) throw new IllegalStateException("Error in function: " + f.toString() + "!");
setDescription(f);
data = null;
LoadFunctionAsyncTask l = new LoadFunctionAsyncTask(f, -BOUNDARIES, BOUNDARIES, DATA_POINTS,
(List<Entry> pointList) -> {
if(data == null) {
ILineDataSet dataSet = new LineDataSet(new ArrayList<>(), null);
dataSet.setValueFormatter(new PointValueFormatter());
dataSet.setHighlightEnabled(true);
for (Entry dataPoint : pointList) {
dataSet.addEntry(dataPoint);
}
data = new LineData(dataSet);
setData(data);
} else {
for (Entry dataPoint : pointList) {
data.addEntry(dataPoint, 0);// 0 is the only dataset
}
data.notifyDataChanged();
notifyDataSetChanged();
invalidate();
}
});
l.execute();
}
private void setDescription(Function f) {
Description desc = new Description();
desc.setText(f.getDescription());
desc.setPosition(16, getBottom() - 16);
setDescription(desc);
}
private class PointValueFormatter implements IValueFormatter {
#Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
return "(" + entry.getX() + ", " + entry.getY() + ")";
}
}
}
OK, apparently chart.invalidate() isn't enough, the Matrix needs to be refreshed:
Matrix matrix = null;
//...
matrix = viewPortHandler.zoom(scaleX, scaleX);
//...
if(matrix != null) {
viewPortHandler.refresh(matrix, chart, true);
}
As a bonus, the last true in refresh() is for invalidate, so no need for chart.invalidate();.

Acumatica How to get the Cash Account Begin or Ending Balance?

When I select a Cash Account in the "Founds Transfers" (CA301000), GL Balance and Avaliable Balance Entries are updated.
Where these amounts come from? I mean, I need to query these field via GI but I cannot figure out the Table's name.
Any Clues?
GL Balance and Available Balance fields are part of the CATransfer DAC.
You won't find them in the CATransfer database table because they are calculated at runtime in CATransfer DAC as unbound fields using the FieldSelecting events of GLBalanceAttribute and CashBalanceAttribute.
You can find out the DAC and Data Field of a UI element by holding Ctl+Alt and clicking on that field.
For reference, here are the GLBalance attributes inCATransfer Dac:
#region InGLBalance
public abstract class inGLBalance : PX.Data.IBqlField
{
}
protected Decimal? _InGLBalance;
[PXDefault(TypeCode.Decimal, "0.0", PersistingCheck = PXPersistingCheck.Nothing)]
[PXCury(typeof(CATransfer.inCuryID))]
[PXUIField(DisplayName = "GL Balance", Enabled = false)]
[GLBalance(typeof(CATransfer.inAccountID), null, typeof(CATransfer.inDate))]
public virtual Decimal? InGLBalance
{
get
{
return this._InGLBalance;
}
set
{
this._InGLBalance = value;
}
}
#endregion
#region OutGLBalance
public abstract class outGLBalance : PX.Data.IBqlField
{
}
protected Decimal? _OutGLBalance;
[PXDefault(TypeCode.Decimal, "0.0", PersistingCheck = PXPersistingCheck.Nothing)]
[PXCury(typeof(CATransfer.outCuryID))]
[PXUIField(DisplayName = "GL Balance", Enabled = false)]
[GLBalance(typeof(CATransfer.outAccountID), null, typeof(CATransfer.outDate))]
public virtual Decimal? OutGLBalance
{
get
{
return this._OutGLBalance;
}
set
{
this._OutGLBalance = value;
}
}
#endregion
Here is the GLBalanceAttribute class FieldSelecting event where the value is calculated:
public virtual void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
GLSetup gLSetup = PXSelect<GLSetup>.Select(sender.Graph);
decimal? result = 0m;
object CashAccountID = sender.GetValue(e.Row, _CashAccount);
object FinPeriodID = null;
if (string.IsNullOrEmpty(_FinPeriodID))
{
object FinDate = sender.GetValue(e.Row, _FinDate);
FinPeriod finPeriod = PXSelect<FinPeriod, Where<FinPeriod.startDate, LessEqual<Required<FinPeriod.startDate>>,
And<FinPeriod.endDate, Greater<Required<FinPeriod.endDate>>>>>.Select(sender.Graph, FinDate, FinDate);
if (finPeriod != null)
{
FinPeriodID = finPeriod.FinPeriodID;
}
}
else
{
FinPeriodID = sender.GetValue(e.Row, _FinPeriodID);
}
if (CashAccountID != null && FinPeriodID != null)
{
// clear glhistory cache for ReleasePayments longrun
sender.Graph.Caches<GLHistory>().ClearQueryCache();
sender.Graph.Caches<GLHistory>().Clear();
GLHistory gLHistory = PXSelectJoin<GLHistory,
InnerJoin<GLHistoryByPeriod,
On<GLHistoryByPeriod.accountID, Equal<GLHistory.accountID>,
And<GLHistoryByPeriod.branchID, Equal<GLHistory.branchID>,
And<GLHistoryByPeriod.ledgerID, Equal<GLHistory.ledgerID>,
And<GLHistoryByPeriod.subID, Equal<GLHistory.subID>,
And<GLHistoryByPeriod.lastActivityPeriod, Equal<GLHistory.finPeriodID>>>>>>,
InnerJoin<Branch,
On<Branch.branchID, Equal<GLHistory.branchID>,
And<Branch.ledgerID, Equal<GLHistory.ledgerID>>>,
InnerJoin<CashAccount,
On<GLHistoryByPeriod.branchID, Equal<CashAccount.branchID>,
And<GLHistoryByPeriod.accountID, Equal<CashAccount.accountID>,
And<GLHistoryByPeriod.subID, Equal<CashAccount.subID>>>>,
InnerJoin<Account,
On<GLHistoryByPeriod.accountID, Equal<Account.accountID>,
And<Match<Account, Current<AccessInfo.userName>>>>,
InnerJoin<Sub,
On<GLHistoryByPeriod.subID, Equal<Sub.subID>, And<Match<Sub, Current<AccessInfo.userName>>>>>>>>>,
Where<CashAccount.cashAccountID, Equal<Required<CashAccount.cashAccountID>>,
And<GLHistoryByPeriod.finPeriodID, Equal<Required<GLHistoryByPeriod.finPeriodID>>>
>>.Select(sender.Graph, CashAccountID, FinPeriodID);
if (gLHistory != null)
{
result = gLHistory.CuryFinYtdBalance;
}
}
e.ReturnValue = result;
e.Cancel = true;
}
}
Here are the CashBalance attributes of CATransfer Dac:
#region CashBalanceIn
public abstract class cashBalanceIn : PX.Data.IBqlField
{
}
protected Decimal? _CashBalanceIn;
[PXDefault(TypeCode.Decimal, "0.0", PersistingCheck = PXPersistingCheck.Nothing)]
[PXCury(typeof(CATransfer.inCuryID))]
[PXUIField(DisplayName = "Available Balance", Enabled = false)]
[CashBalance(typeof(CATransfer.inAccountID))]
public virtual Decimal? CashBalanceIn
{
get
{
return this._CashBalanceIn;
}
set
{
this._CashBalanceIn = value;
}
}
#endregion
#region CashBalanceOut
public abstract class cashBalanceOut : PX.Data.IBqlField
{
}
protected Decimal? _CashBalanceOut;
[PXDefault(TypeCode.Decimal, "0.0", PersistingCheck = PXPersistingCheck.Nothing)]
[PXCury(typeof(CATransfer.outCuryID))]
[PXUIField(DisplayName = "Available Balance", Enabled = false)]
[CashBalance(typeof(CATransfer.outAccountID))]
public virtual Decimal? CashBalanceOut
{
get
{
return this._CashBalanceOut;
}
set
{
this._CashBalanceOut = value;
}
}
#endregion
And the CashBalanceAttribute class FieldSelecting event where the value is calculated:
public virtual void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
CASetup caSetup = PXSelect<CASetup>.Select(sender.Graph);
decimal? result = 0m;
object CashAccountID = sender.GetValue(e.Row, _CashAccount);
CADailySummary caBalance = PXSelectGroupBy<CADailySummary,
Where<CADailySummary.cashAccountID, Equal<Required<CADailySummary.cashAccountID>>>,
Aggregate<Sum<CADailySummary.amtReleasedClearedCr,
Sum<CADailySummary.amtReleasedClearedDr,
Sum<CADailySummary.amtReleasedUnclearedCr,
Sum<CADailySummary.amtReleasedUnclearedDr,
Sum<CADailySummary.amtUnreleasedClearedCr,
Sum<CADailySummary.amtUnreleasedClearedDr,
Sum<CADailySummary.amtUnreleasedUnclearedCr,
Sum<CADailySummary.amtUnreleasedUnclearedDr>>>>>>>>>>.
Select(sender.Graph, CashAccountID);
if ((caBalance != null) && (caBalance.CashAccountID != null))
{
result = caBalance.AmtReleasedClearedDr - caBalance.AmtReleasedClearedCr;
if ((bool)caSetup.CalcBalDebitClearedUnreleased)
result += caBalance.AmtUnreleasedClearedDr;
if ((bool)caSetup.CalcBalCreditClearedUnreleased)
result -= caBalance.AmtUnreleasedClearedCr;
if ((bool)caSetup.CalcBalDebitUnclearedReleased)
result += caBalance.AmtReleasedUnclearedDr;
if ((bool)caSetup.CalcBalCreditUnclearedReleased)
result -= caBalance.AmtReleasedUnclearedCr;
if ((bool)caSetup.CalcBalDebitUnclearedUnreleased)
result += caBalance.AmtUnreleasedUnclearedDr;
if ((bool)caSetup.CalcBalCreditUnclearedUnreleased)
result -= caBalance.AmtUnreleasedUnclearedCr;
}
e.ReturnValue = result;
e.Cancel = true;
}
}

REST based Multiple file Upload With additional Properties(Parameters) C# WCF

I have a WCF service which used multipart/Form-Data, How can i get the properties and the files uploaded
Finally i got my service working.
I have created a multiparser.
public class MultipartParser
{
public MultipartParser(Stream stream)
{
this.Parse(stream, Encoding.UTF8);
}
public static byte[][] Separate(byte[] source, byte[] separator)
{
var Parts = new List<byte[]>();
var Index = 0;
byte[] Part;
for (var I = 0; I < source.Length; ++I)
{
if (Equals(source, separator, I))
{
Part = new byte[I - Index];
Array.Copy(source, Index, Part, 0, Part.Length);
Parts.Add(Part);
Index = I + separator.Length;
I += separator.Length - 1;
}
}
Part = new byte[source.Length - Index];
Array.Copy(source, Index, Part, 0, Part.Length);
Parts.Add(Part);
return Parts.ToArray();
}
public static bool Equals(byte[] source, byte[] separator, int index)
{
for (int i = 0; i < separator.Length; ++i)
if (index + i >= source.Length || source[index + i] != separator[i])
return false;
return true;
}
private void Parse(Stream stream, Encoding encoding)
{
Regex regQuery;
Match regMatch;
string propertyType;
// The first line should contain the delimiter
byte[] data = ToByteArray(stream);
// Copy to a string for header parsing
string content = encoding.GetString(data);
int delimiterEndIndex = content.IndexOf("\r\n");
if (delimiterEndIndex > -1)
{
string delimiterString = content.Substring(0, content.IndexOf("\r\n"));
byte[] delimiterBytes = encoding.GetBytes(delimiterString);
byte[] delimiterWithNewLineBytes = encoding.GetBytes(delimiterString + "\r\n");
// the request ends DELIMITER--\r\n
byte[] delimiterEndBytes = encoding.GetBytes("\r\n" + delimiterString + "--\r\n");
int lengthDifferenceWithEndBytes = (delimiterString + "--\r\n").Length;
byte[][] separatedStream = Separate(data, delimiterWithNewLineBytes);
data = null;
for (int i = 0; i < separatedStream.Length; i++)
{
// parse out whether this is a parameter or a file
// get the first line of the byte[] as a string
string thisPieceAsString = encoding.GetString(separatedStream[i]);
if (string.IsNullOrWhiteSpace(thisPieceAsString)) { continue; }
string firstLine = thisPieceAsString.Substring(0, thisPieceAsString.IndexOf("\r\n"));
// Check the item to see what it is
regQuery = new Regex(#"(?<=name\=\"")(.*?)(?=\"")");
regMatch = regQuery.Match(firstLine);
propertyType = regMatch.Value.Trim();
// get the index of the start of the content and the end of the content
int indexOfStartOfContent = thisPieceAsString.IndexOf("\r\n\r\n") + "\r\n\r\n".Length;
// this line compares the name to the name of the html input control,
// this can be smarter by instead looking for the filename property
StreamContent myContent = new StreamContent();
if (propertyType.ToUpper().Trim() != "FILES")
{
// this is a parameter!
// if this is the last piece, chop off the final delimiter
int lengthToRemove = (i == separatedStream.Length - 1) ? lengthDifferenceWithEndBytes : 0;
string value = thisPieceAsString.Substring(indexOfStartOfContent, thisPieceAsString.Length - "\r\n".Length - indexOfStartOfContent - lengthToRemove);
myContent.StringData = value;
myContent.PropertyName = propertyType;
if (StreamContents == null)
StreamContents = new List<StreamContent>();
StreamContents.Add(myContent);
this.Success = true;
}
else
{
// this is a file!
regQuery = new Regex(#"(?<=filename\=\"")(.*?)(?=\"")");
regMatch = regQuery.Match(firstLine);
string fileName = regMatch.Value.Trim();
// get the content byte[]
// if this is the last piece, chop off the final delimiter
int lengthToRemove = (i == separatedStream.Length - 1) ? delimiterEndBytes.Length : 0;
int contentByteArrayStartIndex = encoding.GetBytes(thisPieceAsString.Substring(0, indexOfStartOfContent)).Length;
byte[] fileData = new byte[separatedStream[i].Length - contentByteArrayStartIndex - lengthToRemove];
Array.Copy(separatedStream[i], contentByteArrayStartIndex, fileData, 0, separatedStream[i].Length - contentByteArrayStartIndex - lengthToRemove);
// save the fileData byte[] as the file
myContent.PropertyName = propertyType;
myContent.FileName = fileName;
myContent.IsFile = true;
myContent.Data = fileData;
if (StreamContents == null)
StreamContents = new List<StreamContent>();
StreamContents.Add(myContent);
this.Success = true;
}
}
}
}
private byte[] ToByteArray(Stream stream)
{
byte[] buffer = new byte[32768];
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}
public List<StreamContent> StreamContents { get; set; }
public bool Success
{
get;
private set;
}
public string ContentType
{
get;
private set;
}
public string Filename
{
get;
private set;
}
public List<byte[]> FileContents
{
get;
private set;
}
}
public class StreamContent
{
public byte[] Data { get; set; }
public bool IsFile { get; set; }
public string FileType { get; set; }
public string FileName { get; set; }
public string PropertyName { get; set; }
public string StringData { get; set; }
}`
Using this parser u could get the properties and their values in the List object. In the service Use the following
MultipartParser parser = new MultipartParser(stream);
if (parser != null && parser.Success)
{
foreach (var content in parser.StreamContents)
{
if (content.IsFile) // if file
{
string path = "Your Path";
//content.FileName; file Name
//content.Data File contents in byte[]
// Write the file to the path specifies
File.WriteAllBytes(path+content.FileName, content.Data);
continue;
}
//content.PropertyName; gives u the Parameter
//content.StringData; gives u the value of the parameter
}
}

Extract common objects from an arraylist

I have a list like shown below. Assume it has 16 Container objects in it. Each Container object is a simple bean, with fields like age, weight, height, etc. How can I create a sub-list that contains common 'Container' objects if a 'Container' object is considered equal if the weight and height are equal?
List<Container> containers = new ArrayList<Container>();
If by "common" containers you mean duplicating ones, then this code might help you:
import java.util.ArrayList;
import java.util.List;
public class CommonContainers {
public static void main(String[] args) {
List<Container> containers = new ArrayList<Container>(16);
for(int i=0; i<13; i++) {
containers.add(new Container(i, i));
}
//add a few duplicating ones
containers.add(new Container(1,1));
containers.add(new Container(5,5));
containers.add(new Container(6,6));
List<Container> sublist = new ArrayList<Container>();
for (Container c1 : containers) {
for (Container c2 : containers) {
if(c1 != c2 && c1.equals(c2)) {
sublist.add(c1);
}
}
}
for (Container c : sublist) {
System.out.println(c);
}
}
private static class Container {
private int weight;
private int height;
#Override
public String toString() {
return String.format("Container[w=%d,h=%d]", weight, height);
}
public Container(int weight, int height) {
this.weight = weight;
this.height = height;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + height;
result = prime * result + weight;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Container other = (Container) obj;
if (height != other.height)
return false;
if (weight != other.weight)
return false;
return true;
}
}
}
If you mean something else or need clarification, please let me know.
Thanks John Smith for giving direction on this question. I used the iterator instead and was able to make a nice solution to what I was looking for. below is the solution. Note that .equals is overriden for the Containers comparison. The technique I used will take the master list and create a sub-list while removing elements from the parent list at the same time. The solution can be called recursivly until you convert the master list into a subset of lists.
public List<Container> extractCommonSubList(
List<Container> masterContainerList) {
List<Container> subList = new ArrayList<Container>();
ListIterator<Container> iterator = masterContainerList.listIterator();
// get first item from master list and remove from master list
Container firstContainer = iterator.next();
iterator.remove();
// Add first container to sublist
subList.add(firstContainer);
while (iterator.hasNext()) {
Container container = iterator.next();
// Search for matches
if (firstContainer.equals(container)) {
// containers are a match, continue searching for matches
subList.add(container);
iterator.remove();
continue;
} else {
break;
}
}
// return common list
return subList;
}

Adding message to faceContext is not working in Java EE7 run on glassFish?

I am doing the tutorial on Java EE7 that comes with glassFish installation. It is also available here. The code is present in glassFish server installation directory
/glassFish_installation/glassfish4/docs/javaee-tutorial/examples/cdi/guessnumber-cdi.
The code works fine as it is. It currently displays correct! when a user correctly guesses the number but does not display failed at end of the game. so I introduced, just one minor change to display the failed message. I have added comments right above the relevant change in code.
Somehow, this change did not help. That is, the at the end of the game, failed message is not displayed.
But the game works as usual. I would like to know why this did not work and how to correct it?
Thanks
public class UserNumberBean implements Serializable {
private static final long serialVersionUID = -7698506329160109476L;
private int number;
private Integer userNumber;
private int minimum;
private int remainingGuesses;
#Inject
#MaxNumber
private int maxNumber;
private int maximum;
#Inject
#Random
Instance<Integer> randomInt;
public UserNumberBean() {
}
public int getNumber() {
return number;
}
public void setUserNumber(Integer user_number) {
userNumber = user_number;
}
public Integer getUserNumber() {
return userNumber;
}
public int getMaximum() {
return (this.maximum);
}
public void setMaximum(int maximum) {
this.maximum = maximum;
}
public int getMinimum() {
return (this.minimum);
}
public void setMinimum(int minimum) {
this.minimum = minimum;
}
public int getRemainingGuesses() {
return remainingGuesses;
}
public String check() throws InterruptedException {
if (userNumber > number) {
maximum = userNumber - 1;
}
if (userNumber < number) {
minimum = userNumber + 1;
}
if (userNumber == number) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("Correct!"));
}
//if remainingGuesses is less than or equal to zero, display failed message
//-----------------------------------------------
if (remainingGuesses-- <= 0) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("failed "));
}
return null;
}
#PostConstruct
public void reset() {
this.minimum = 0;
this.userNumber = 0;
this.remainingGuesses = 10;
this.maximum = maxNumber;
this.number = randomInt.get();
}
public void validateNumberRange(FacesContext context,
UIComponent toValidate,
Object value) {
int input = (Integer) value;
if (input < minimum || input > maximum) {
((UIInput) toValidate).setValid(false);
FacesMessage message = new FacesMessage("Invalid guess");
context.addMessage(toValidate.getClientId(context), message);
}
}
}
Adding the FacesMessage is actually working, the problem is that you are using postdecrement in your condition.
Postdecrement, as the name suggests, is decremented AFTER the execution of the statement containing the postdecrement.
That means, if you write:
if (remainingGuesses-- <= 0) {
the var remainingGuesses is decremented after the if-condition was evaluated.
In your case, when the last guess is checked, remainingGuesses is actually 1 and therefore the if-condition is not true and the message is not added.
Different obvious solutions:
if (remainingGuesses-- <= 1) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("failed "));
}
or
if (--remainingGuesses <= 0) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("failed "));
}
or
remainingGuesses--;
if (remainingGuesses <= 0) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("failed "));
}
See also:
Is there a difference between x++ and ++x in java?
Difference between i++ and ++i in a loop?